forked from yvv/decision
Lot S, phase 2 : le quorum dans l'interface (fr, en, es), e2e 35 / 35
- types.ts : evaluation.quorum / yesNeeded / noNeeded, levels[].quorum, l'unité quorum / quorumDefault / canSetQuorum, doc.quorums (optionnels) ; publication « suspended » (ADR 0011) ; - Réglages de la formule : la colonne « Quorum » des cinq crans et sa note, la participation de référence « deux fois 50, le quorum de référence » ; plus de ligne C ; sur téléphone, les six colonnes tiennent dans la feuille ; - La formule du seuil : T' = max(T, Q), R = B^W + ratio(T')·T', la ligne Q (le quorum de la version) au lieu de C ; - textes : deep.note, formula.reading, settings.* parlent du quorum, plus de C (test vu rouge puis vert, tests/unit/inertia.test.ts) ; - e2e : Seuil 9 (cran 2) et 39 (cran 3) dans Monnaie libre, les quorums 12 · 12 · 50 · 50 · 50, voix pour à 100 votants 60 · 67 · 75 · 83 · 90, à 0 votant 8 · 9 · 39 · 43 · 46. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -83,7 +83,7 @@ test('M1 — mobile : logo dans la barre, l’accueil sous la voûte, menu en ti
|
||||
// in force at the start, its vote unfolds in the list of proposals).
|
||||
await page.locator('ul.deep button.row').first().click();
|
||||
const fold = page.locator('ul.deep section.fold');
|
||||
await expect(fold.getByText('Seuil : 50 voix pour')).toBeVisible();
|
||||
await expect(fold.getByText('Seuil : 9 voix pour')).toBeVisible();
|
||||
// The settings of the formula read on a phone: the table of the five levels holds in the sheet.
|
||||
await fold.getByRole('button', { name: 'Réglages de la formule' }).click();
|
||||
const panel = page.locator('aside.panel');
|
||||
|
||||
@@ -190,7 +190,7 @@ test('C27 — la participation ramenée dans ses bornes : la mesure, puis ce que
|
||||
await expect(panel.getByText(/^3 votants sur 4 \(62,5\s%\) : la médiane des 12 propositions votées depuis un an\.$/)).toBeVisible();
|
||||
// Then what the group asks, the group as subject.
|
||||
await expect(panel.getByText(/^Ramenée entre 5\s% et 50\s% de l'électorat : le groupe demande ses voix pour comme si 2 personnes votaient \(50\s%\)\.$/)).toBeVisible();
|
||||
await expect(panel.getByText("Part exigée : les « pour » demandés au-delà de C, à 2 votants comme d'habitude.")).toBeVisible();
|
||||
await expect(panel.getByText("Part exigée : la part de « pour » demandée, à 2 votants comme d'habitude.")).toBeVisible();
|
||||
await shot(page, 'c27-participation-bornee');
|
||||
});
|
||||
|
||||
|
||||
+14
-8
@@ -283,7 +283,8 @@ test('C19 — un article, une ligne : ses versions se déplient', async ({ page
|
||||
// A version unfolds its vote below it, as any proposal.
|
||||
await versions.nth(1).click();
|
||||
await expect(versions.nth(1)).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(page.locator('section.fold').getByText('Seuil : 50 voix pour')).toBeVisible();
|
||||
// Level 3, the quorum 50 (ADR 0013): 39 « pour » at no vote, the voters missing counted as « non ».
|
||||
await expect(page.locator('section.fold').getByText('Seuil : 39 voix pour')).toBeVisible();
|
||||
await expect(page).toHaveURL(/vote=/);
|
||||
await e3.click();
|
||||
await expect(e3).toHaveAttribute('aria-expanded', 'false');
|
||||
@@ -299,19 +300,22 @@ test('C20 — réglages de la formule et cinq crans', async ({ page }) => {
|
||||
const panel = page.locator('aside.panel');
|
||||
await expect(panel.getByRole('heading', { name: 'Réglages de la formule' })).toBeVisible();
|
||||
await expect(panel.getByText(/W — électorat : 5\s745/)).toBeVisible();
|
||||
await expect(panel.getByText('C — minimum de voix pour : 50')).toBeVisible();
|
||||
// No vote yet: the reference, twice C.
|
||||
await expect(panel.getByText(/100 votants sur 5\s745 \(1,74 %\), deux fois C : la participation de référence/)).toBeVisible();
|
||||
// No vote yet: the reference, twice the reference quorum (ADR 0013).
|
||||
await expect(panel.getByText(/100 votants sur 5\s745 \(1,74 %\), deux fois 50, le quorum de référence : la participation de référence/)).toBeVisible();
|
||||
const rows = panel.locator('table tbody tr');
|
||||
await expect(rows).toHaveCount(5);
|
||||
await expect(rows.locator('th')).toHaveText([/1\s*très basse/, /2\s*basse/, /3\s*moyenne/, /4\s*haute/, /5\s*très haute/]);
|
||||
await expect(rows.locator('td:nth-child(2)')).toHaveText(['50 %', '50 %', '55 %', '60 %', '66 %']);
|
||||
await expect(rows.locator('td:nth-child(4)')).toHaveText(['60 %', '67 %', '75 %', '83 %', '90 %']);
|
||||
// At 100 voters: C plus each level's share of the 50 votes beyond C.
|
||||
// The quorum of each level (Yvv: « 12 · 12 · 50 · 50 · 50 »).
|
||||
const quorums = rows.locator('td:nth-child(5)');
|
||||
await expect(quorums).toHaveText(['12', '12', '50', '50', '50']);
|
||||
// At 100 voters, above every quorum: each level's share of the 100.
|
||||
const required = panel.locator('table tbody td.required');
|
||||
await expect(required).toHaveText(['80', '84', '88', '92', '95']);
|
||||
await expect(required).toHaveText(['60', '67', '75', '83', '90']);
|
||||
await expect(panel.getByText("Voix pour : à 100 votants, comme d'habitude.")).toBeVisible();
|
||||
await expect(panel.getByText("Part exigée : les « pour » demandés au-delà de C, à 100 votants comme d'habitude.")).toBeVisible();
|
||||
await expect(panel.getByText("Part exigée : la part de « pour » demandée, à 100 votants comme d'habitude.")).toBeVisible();
|
||||
await expect(panel.getByText(/^Quorum : en dessous, les votants qui manquent comptent comme « non »\./)).toBeVisible();
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
// From the vote of an article: Unicité du compte, a seed proposal, is at 2 / 5, its row is marked.
|
||||
@@ -322,7 +326,9 @@ test('C20 — réglages de la formule et cinq crans', async ({ page }) => {
|
||||
await expect(panel.locator('tr.current')).toHaveCount(1);
|
||||
await expect(panel.locator('tr.current th')).toContainText('2');
|
||||
await expect(panel.locator('tr.current th')).toContainText('cet article');
|
||||
await expect(required).toHaveText(['50', '50', '50', '50', '50']);
|
||||
// No vote: each level on its quorum, the voters missing counted as « non ».
|
||||
await expect(quorums).toHaveText(['12', '12', '50', '50', '50']);
|
||||
await expect(required).toHaveText(['8', '9', '39', '43', '46']);
|
||||
await expect(panel.getByText('Voix pour : à 0 votant, la participation de cette version.')).toBeVisible();
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
|
||||
@@ -195,7 +195,8 @@ test('C24 — sans compte de vote, le vote par la chaîne est fermé et le dit ;
|
||||
await expect(fold.getByText("Le vote par la chaîne Ğ1 n'est pas encore ouvert : le compte qui reçoit les votes reste à créer.", { exact: false })).toBeVisible();
|
||||
await expect(fold.getByRole('button', { name: 'Pour', exact: true })).toHaveCount(0);
|
||||
// The totals and the threshold stay in sight.
|
||||
await expect(fold.getByText('Seuil : 50 voix pour')).toBeVisible();
|
||||
// Raison d'être, level 2, the quorum 12 (ADR 0013).
|
||||
await expect(fold.getByText('Seuil : 9 voix pour')).toBeVisible();
|
||||
await fold.getByText('Ton vote ne se montre à personne', { exact: false }).scrollIntoViewIfNeeded();
|
||||
await shot(page, '6-vote-ferme');
|
||||
// The basket, opened by its address, says the vote is closed — never « Tes votes comptent dès l'envoi ».
|
||||
|
||||
@@ -33,12 +33,18 @@
|
||||
<details class="formula">
|
||||
<summary>{t('formula.title')}</summary>
|
||||
<p class="muted">{t('formula.intro')}</p>
|
||||
<!-- ADR 0013: judged on T′ = max(T, Q) voters; below the quorum, the voters missing count as against. -->
|
||||
<math display="block" class="math">
|
||||
<mrow>
|
||||
<mi>R</mi><mo>=</mo><mi>C</mi><mo>+</mo><msup><mi>B</mi><mi>W</mi></msup><mo>+</mo>
|
||||
<msup><mi>T</mi><mo>′</mo></msup><mo>=</mo><mi>max</mi><mo>(</mo><mi>T</mi><mo>,</mo><mi>Q</mi><mo>)</mo>
|
||||
</mrow>
|
||||
</math>
|
||||
<math display="block" class="math">
|
||||
<mrow>
|
||||
<mi>R</mi><mo>=</mo><msup><mi>B</mi><mi>W</mi></msup><mo>+</mo>
|
||||
<mrow><mo>(</mo><mi>M</mi><mo>+</mo><mo>(</mo><mn>1</mn><mo>−</mo><mi>M</mi><mo>)</mo><mo>(</mo><mn>1</mn><mo>−</mo>
|
||||
<msup><mrow><mo>(</mo><mfrac><mi>T</mi><mi>W</mi></mfrac><mo>)</mo></mrow><mi>G</mi></msup><mo>)</mo><mo>)</mo></mrow>
|
||||
<mo>·</mo><mi>max</mi><mo>(</mo><mn>0</mn><mo>,</mo><mi>T</mi><mo>−</mo><mi>C</mi><mo>)</mo>
|
||||
<msup><mrow><mo>(</mo><mfrac><msup><mi>T</mi><mo>′</mo></msup><mi>W</mi></mfrac><mo>)</mo></mrow><mi>G</mi></msup><mo>)</mo><mo>)</mo></mrow>
|
||||
<mo>·</mo><msup><mi>T</mi><mo>′</mo></msup>
|
||||
</mrow>
|
||||
</math>
|
||||
<ul class="values">
|
||||
@@ -49,7 +55,7 @@
|
||||
<li>{t('formula.G', { n: i18n.number(Math.round(p.gradient * 1000) / 1000) })}</li>
|
||||
{/if}
|
||||
<li>{t('formula.B', { n: i18n.number(base) })}</li>
|
||||
<li>{t('formula.C', { n: i18n.number(floor) })}</li>
|
||||
<li>{t('formula.Q', { n: i18n.number(evaluation?.quorum ?? p?.quorum ?? floor) })}</li>
|
||||
{#if evaluation}<li class="r">{t('formula.R', { n: evaluation.required })}</li>{/if}
|
||||
</ul>
|
||||
<p class="faint">{t('formula.reading')}</p>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
// The settings of the formula, read only: W and its source, C, B, the usual
|
||||
// The settings of the formula, read only: W and its source, B, the usual
|
||||
// turnout of the group (median or reference) and the five levels on its
|
||||
// scale. From the head of the document, the *for* votes asked are those of
|
||||
// the usual turnout; from a version, those of its own turnout.
|
||||
// scale, each with its quorum (ADR 0013). From the head of the document, the
|
||||
// *for* votes asked are those of the usual turnout; from a version, those of
|
||||
// its own turnout, on its article's quorum.
|
||||
import { api } from '$lib/api';
|
||||
import type { DocState } from '$lib/doc.svelte';
|
||||
import { i18n, t } from '$lib/i18n/index.svelte';
|
||||
@@ -50,7 +51,6 @@
|
||||
|
||||
<ul class="values">
|
||||
<li>{t('formula.W', { n: i18n.number(view.doc.electorate), source: t(view.doc.electorateSource === 'wot' ? 'formula.W.wot' : 'formula.W.group') })}</li>
|
||||
<li>{t('formula.C', { n: i18n.number(view.doc.voteFloor) })}</li>
|
||||
<li>{t('formula.B', { n: i18n.number(view.doc.formula.base) })}</li>
|
||||
</ul>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
{#if p.source === 'median'}
|
||||
{i18n.plural('settings.usual.median', shown?.voters ?? p.voters, { electorate: i18n.number(view.doc.electorate), pct: pct(shown?.ratio ?? p.ratio), sample: p.sample })}
|
||||
{:else}
|
||||
{i18n.plural('settings.usual.reference', shown?.voters ?? p.voters, { electorate: i18n.number(view.doc.electorate), pct: pct(shown?.ratio ?? p.ratio), min: p.minSample, sample: p.sample })}
|
||||
{i18n.plural('settings.usual.reference', shown?.voters ?? p.voters, { electorate: i18n.number(view.doc.electorate), pct: pct(shown?.ratio ?? p.ratio), q: i18n.number(view.doc.voteFloor), min: p.minSample, sample: p.sample })}
|
||||
{/if}
|
||||
</p>
|
||||
{#if p.bounded}<p class="faint small">{i18n.plural('settings.usual.bounded', p.voters, { lo: pct(p.bounds[0]), hi: pct(p.bounds[1]), pct: pct(p.ratio) })}</p>{/if}
|
||||
@@ -76,6 +76,7 @@
|
||||
<th scope="col">M</th>
|
||||
<th scope="col">G</th>
|
||||
<th scope="col">{t('settings.col.share')}</th>
|
||||
<th scope="col">{t('settings.col.quorum')}</th>
|
||||
<th scope="col">{t('settings.col.required')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -92,6 +93,7 @@
|
||||
<td>{whole(l.majorityPct)}</td>
|
||||
<td>{gradient(l.gradient)}</td>
|
||||
<td>{whole(l.sharePct)}</td>
|
||||
<td>{l.quorum === undefined ? '—' : i18n.number(l.quorum)}</td>
|
||||
<td class="required">{l.required === null ? '—' : i18n.number(l.required)}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
@@ -99,6 +101,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<p class="faint small">{i18n.plural('settings.share.note', p.voters)}</p>
|
||||
<p class="faint small">{t('settings.quorum.note')}</p>
|
||||
<p class="faint small">{i18n.plural(version ? 'settings.required.version' : 'settings.required.usual', cast)}</p>
|
||||
<p class="faint small">{t('settings.reading')}</p>
|
||||
</section>
|
||||
@@ -194,4 +197,15 @@
|
||||
.tag {
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
/* On a phone, six columns hold in the sheet: the headers and the level's name wrap. */
|
||||
@media (max-width: 480px) {
|
||||
th,
|
||||
td {
|
||||
padding: 0.4rem 0.3rem;
|
||||
}
|
||||
thead th,
|
||||
th[scope='row'] {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+10
-8
@@ -137,7 +137,7 @@ export const en: Messages = {
|
||||
'doc.publish.pending': 'Publication pending',
|
||||
|
||||
'deep.title': 'Open proposals',
|
||||
'deep.note': 'Not in force yet. Each one moves up as soon as it reaches its threshold: at least {floor} votes in favor, then the majority its inertia requires.',
|
||||
'deep.note': 'Not in force yet. Each one moves up as soon as it reaches its threshold: the share of votes in favour its inertia requires, counted on at least its quorum of voters.',
|
||||
'deep.new': 'Propose a new article',
|
||||
'deep.drafts': 'Your drafts',
|
||||
'deep.drafts.note': 'Not submitted to vote. Only you can see them.',
|
||||
@@ -375,17 +375,17 @@ export const en: Messages = {
|
||||
'formula.M': 'M — majority at full turnout: {n}%',
|
||||
'formula.G': 'G — gradient: {n}',
|
||||
'formula.B': 'B — base: {n}',
|
||||
'formula.C': 'C — minimum votes in favor: {n}',
|
||||
'formula.Q': 'Q — quorum: {n}',
|
||||
'formula.R': 'R — current threshold: {n} votes in favor',
|
||||
'formula.reading': 'At low turnout, near-unanimity is needed; when everyone votes, majority M is enough. Nothing passes with fewer than C votes in favor.',
|
||||
'formula.reading': 'At low turnout, near-unanimity is needed; when everyone votes, majority M is enough. Below the quorum Q, the missing voters count as against.',
|
||||
'common.pct': '{n}%',
|
||||
'settings.title': 'Formula settings',
|
||||
'settings.intro': 'To change a text, the group asks for a number of votes in favour that depends on its electorate, its usual turnout and the inertia of the article.',
|
||||
'settings.usual': 'Usual turnout',
|
||||
'settings.usual.median.one': '{n} voter out of {electorate} ({pct}): the median of the {sample} proposals voted on over the past year.',
|
||||
'settings.usual.median.other': '{n} voters out of {electorate} ({pct}): the median of the {sample} proposals voted on over the past year.',
|
||||
'settings.usual.reference.one': '{n} voter out of {electorate} ({pct}), twice C: the reference turnout, until the group has voted on {min} proposals ({sample} so far).',
|
||||
'settings.usual.reference.other': '{n} voters out of {electorate} ({pct}), twice C: the reference turnout, until the group has voted on {min} proposals ({sample} so far).',
|
||||
'settings.usual.reference.one': '{n} voter out of {electorate} ({pct}), twice {q}, the reference quorum: the reference turnout, until the group has voted on {min} proposals ({sample} so far).',
|
||||
'settings.usual.reference.other': '{n} voters out of {electorate} ({pct}), twice {q}, the reference quorum: the reference turnout, until the group has voted on {min} proposals ({sample} so far).',
|
||||
'settings.usual.bounded.one': 'Kept between {lo} and {hi} of the electorate: the group asks for its votes in favour as if {n} person voted ({pct}).',
|
||||
'settings.usual.bounded.other': 'Kept between {lo} and {hi} of the electorate: the group asks for its votes in favour as if {n} people voted ({pct}).',
|
||||
'settings.usual.when': 'Measured once a day, last on {date}.',
|
||||
@@ -393,15 +393,17 @@ export const en: Messages = {
|
||||
'settings.levels': 'The five levels',
|
||||
'settings.col.level': 'Level',
|
||||
'settings.col.share': 'Share asked',
|
||||
'settings.col.quorum': 'Quorum',
|
||||
'settings.col.required': 'Votes for',
|
||||
'settings.current': 'this article',
|
||||
'settings.share.note.one': 'Share asked: the votes in favour asked beyond C, at {n} voter as usual.',
|
||||
'settings.share.note.other': 'Share asked: the votes in favour asked beyond C, at {n} voters as usual.',
|
||||
'settings.share.note.one': 'Share asked: the share of votes in favour asked, at {n} voter as usual.',
|
||||
'settings.share.note.other': 'Share asked: the share of votes in favour asked, at {n} voters as usual.',
|
||||
'settings.quorum.note': "Quorum: below it, the missing voters count as against. It is the level's; the author of an article may set another one before the first vote.",
|
||||
'settings.required.version.one': "Votes for: at {n} voter, this version's turnout.",
|
||||
'settings.required.version.other': "Votes for: at {n} voters, this version's turnout.",
|
||||
'settings.required.usual.one': 'Votes for: at {n} voter, as usual.',
|
||||
'settings.required.usual.other': 'Votes for: at {n} voters, as usual.',
|
||||
'settings.reading': 'At each level, the group asks for its share of votes in favour beyond C when it votes as usual; the more voters, the closer it comes to M. Nothing passes below C votes in favour.',
|
||||
'settings.reading': 'At each level, the group asks for its share of votes in favour when it votes as usual; the more voters, the closer it comes to M. Below the quorum of the level, the missing voters count as against.',
|
||||
|
||||
'history.title': 'Corrections',
|
||||
'history.initial': 'initial text',
|
||||
|
||||
+10
-8
@@ -137,7 +137,7 @@ export const es: Messages = {
|
||||
'doc.publish.pending': 'Publicación pendiente',
|
||||
|
||||
'deep.title': 'Propuestas en curso',
|
||||
'deep.note': 'Aún no están en vigor. Cada una sube en cuanto alcanza su umbral: al menos {floor} votos a favor y, después, la mayoría que exige su inercia.',
|
||||
'deep.note': 'Aún no están en vigor. Cada una sube en cuanto alcanza su umbral: la proporción de votos a favor que exige su inercia, contada sobre al menos su quórum de votantes.',
|
||||
'deep.new': 'Proponer un artículo nuevo',
|
||||
'deep.drafts': 'Tus borradores',
|
||||
'deep.drafts.note': 'No sometidos a votación. Solo tú los ves.',
|
||||
@@ -375,17 +375,17 @@ export const es: Messages = {
|
||||
'formula.M': 'M — mayoría con participación plena: {n} %',
|
||||
'formula.G': 'G — gradiente: {n}',
|
||||
'formula.B': 'B — base: {n}',
|
||||
'formula.C': 'C — mínimo de votos a favor: {n}',
|
||||
'formula.Q': 'Q — quórum: {n}',
|
||||
'formula.R': 'R — umbral actual: {n} votos a favor',
|
||||
'formula.reading': 'Con poca participación, hace falta casi la unanimidad; cuando vota todo el mundo, basta la mayoría M. Nada pasa con menos de C votos a favor.',
|
||||
'formula.reading': 'Con poca participación, hace falta casi la unanimidad; cuando vota todo el mundo, basta la mayoría M. Por debajo del quórum Q, los votantes que faltan cuentan como «no».',
|
||||
'common.pct': '{n} %',
|
||||
'settings.title': 'Ajustes de la fórmula',
|
||||
'settings.intro': 'Para cambiar un texto, el grupo pide un número de votos a favor que depende de su electorado, de su participación habitual y de la inercia del artículo.',
|
||||
'settings.usual': 'Participación habitual',
|
||||
'settings.usual.median.one': '{n} votante de {electorate} ({pct}): la mediana de las {sample} propuestas votadas en el último año.',
|
||||
'settings.usual.median.other': '{n} votantes de {electorate} ({pct}): la mediana de las {sample} propuestas votadas en el último año.',
|
||||
'settings.usual.reference.one': '{n} votante de {electorate} ({pct}), dos veces C: la participación de referencia, hasta que el grupo haya votado {min} propuestas ({sample} por ahora).',
|
||||
'settings.usual.reference.other': '{n} votantes de {electorate} ({pct}), dos veces C: la participación de referencia, hasta que el grupo haya votado {min} propuestas ({sample} por ahora).',
|
||||
'settings.usual.reference.one': '{n} votante de {electorate} ({pct}), dos veces {q}, el quórum de referencia: la participación de referencia, hasta que el grupo haya votado {min} propuestas ({sample} por ahora).',
|
||||
'settings.usual.reference.other': '{n} votantes de {electorate} ({pct}), dos veces {q}, el quórum de referencia: la participación de referencia, hasta que el grupo haya votado {min} propuestas ({sample} por ahora).',
|
||||
'settings.usual.bounded.one': 'Mantenida entre el {lo} y el {hi} del electorado: el grupo pide sus votos a favor como si votara {n} persona ({pct}).',
|
||||
'settings.usual.bounded.other': 'Mantenida entre el {lo} y el {hi} del electorado: el grupo pide sus votos a favor como si votaran {n} personas ({pct}).',
|
||||
'settings.usual.when': 'Medida una vez al día, la última el {date}.',
|
||||
@@ -393,15 +393,17 @@ export const es: Messages = {
|
||||
'settings.levels': 'Los cinco niveles',
|
||||
'settings.col.level': 'Nivel',
|
||||
'settings.col.share': 'Proporción exigida',
|
||||
'settings.col.quorum': 'Quórum',
|
||||
'settings.col.required': 'Votos a favor',
|
||||
'settings.current': 'este artículo',
|
||||
'settings.share.note.one': 'Proporción exigida: los votos a favor pedidos más allá de C, con {n} votante como de costumbre.',
|
||||
'settings.share.note.other': 'Proporción exigida: los votos a favor pedidos más allá de C, con {n} votantes como de costumbre.',
|
||||
'settings.share.note.one': 'Proporción exigida: la parte de votos a favor pedida, con {n} votante como de costumbre.',
|
||||
'settings.share.note.other': 'Proporción exigida: la parte de votos a favor pedida, con {n} votantes como de costumbre.',
|
||||
'settings.quorum.note': 'Quórum: por debajo, los votantes que faltan cuentan como «no». Es el del nivel; el autor de un artículo puede fijar otro antes del primer voto.',
|
||||
'settings.required.version.one': 'Votos a favor: con {n} votante, la participación de esta versión.',
|
||||
'settings.required.version.other': 'Votos a favor: con {n} votantes, la participación de esta versión.',
|
||||
'settings.required.usual.one': 'Votos a favor: con {n} votante, como de costumbre.',
|
||||
'settings.required.usual.other': 'Votos a favor: con {n} votantes, como de costumbre.',
|
||||
'settings.reading': 'En cada nivel, el grupo pide su parte de votos a favor más allá de C cuando vota como de costumbre; cuantos más votantes, más se acerca a M. Nada pasa con menos de C votos a favor.',
|
||||
'settings.reading': 'En cada nivel, el grupo pide su parte de votos a favor cuando vota como de costumbre; cuantos más votantes, más se acerca a M. Por debajo del quórum del nivel, los votantes que faltan cuentan como «no».',
|
||||
|
||||
'history.title': 'Correcciones',
|
||||
'history.initial': 'texto inicial',
|
||||
|
||||
+10
-8
@@ -141,7 +141,7 @@ export const fr = {
|
||||
'doc.publish.pending': 'Publication en attente',
|
||||
|
||||
'deep.title': 'Propositions en cours',
|
||||
'deep.note': "Pas encore en vigueur. Chacune remonte dès qu'elle atteint son seuil : au moins {floor} voix pour, puis la majorité que demande son inertie.",
|
||||
'deep.note': "Pas encore en vigueur. Chacune remonte dès qu'elle atteint son seuil : la part de voix pour que demande son inertie, comptée sur au moins son quorum de votants.",
|
||||
'deep.new': 'Proposer un nouvel article',
|
||||
'deep.drafts': 'Tes brouillons',
|
||||
'deep.drafts.note': 'Non soumis au vote. Toi seul les vois.',
|
||||
@@ -379,17 +379,17 @@ export const fr = {
|
||||
'formula.M': 'M — majorité à pleine participation : {n} %',
|
||||
'formula.G': 'G — gradient : {n}',
|
||||
'formula.B': 'B — base : {n}',
|
||||
'formula.C': 'C — minimum de voix pour : {n}',
|
||||
'formula.Q': 'Q — quorum : {n}',
|
||||
'formula.R': 'R — seuil actuel : {n} voix pour',
|
||||
'formula.reading': "À faible participation, il faut presque l'unanimité ; quand tout le monde vote, la majorité M suffit. Rien ne passe sous C voix pour.",
|
||||
'formula.reading': "À faible participation, il faut presque l'unanimité ; quand tout le monde vote, la majorité M suffit. Sous le quorum Q, les votants qui manquent comptent comme « non ».",
|
||||
'common.pct': '{n} %',
|
||||
'settings.title': 'Réglages de la formule',
|
||||
'settings.intro': "Pour changer un texte, le groupe demande un nombre de voix pour qui dépend de son électorat, de sa participation habituelle et de l'inertie de l'article.",
|
||||
'settings.usual': 'Participation habituelle',
|
||||
'settings.usual.median.one': '{n} votant sur {electorate} ({pct}) : la médiane des {sample} propositions votées depuis un an.',
|
||||
'settings.usual.median.other': '{n} votants sur {electorate} ({pct}) : la médiane des {sample} propositions votées depuis un an.',
|
||||
'settings.usual.reference.one': "{n} votant sur {electorate} ({pct}), deux fois C : la participation de référence, tant que le groupe n'a pas voté sur {min} propositions ({sample} pour l'instant).",
|
||||
'settings.usual.reference.other': "{n} votants sur {electorate} ({pct}), deux fois C : la participation de référence, tant que le groupe n'a pas voté sur {min} propositions ({sample} pour l'instant).",
|
||||
'settings.usual.reference.one': "{n} votant sur {electorate} ({pct}), deux fois {q}, le quorum de référence : la participation de référence, tant que le groupe n'a pas voté sur {min} propositions ({sample} pour l'instant).",
|
||||
'settings.usual.reference.other': "{n} votants sur {electorate} ({pct}), deux fois {q}, le quorum de référence : la participation de référence, tant que le groupe n'a pas voté sur {min} propositions ({sample} pour l'instant).",
|
||||
'settings.usual.bounded.one': "Ramenée entre {lo} et {hi} de l'électorat : le groupe demande ses voix pour comme si {n} personne votait ({pct}).",
|
||||
'settings.usual.bounded.other': "Ramenée entre {lo} et {hi} de l'électorat : le groupe demande ses voix pour comme si {n} personnes votaient ({pct}).",
|
||||
'settings.usual.when': 'Mesurée une fois par jour, la dernière fois le {date}.',
|
||||
@@ -397,15 +397,17 @@ export const fr = {
|
||||
'settings.levels': 'Les cinq crans',
|
||||
'settings.col.level': 'Cran',
|
||||
'settings.col.share': 'Part exigée',
|
||||
'settings.col.quorum': 'Quorum',
|
||||
'settings.col.required': 'Voix pour',
|
||||
'settings.current': 'cet article',
|
||||
'settings.share.note.one': "Part exigée : les « pour » demandés au-delà de C, à {n} votant comme d'habitude.",
|
||||
'settings.share.note.other': "Part exigée : les « pour » demandés au-delà de C, à {n} votants comme d'habitude.",
|
||||
'settings.share.note.one': "Part exigée : la part de « pour » demandée, à {n} votant comme d'habitude.",
|
||||
'settings.share.note.other': "Part exigée : la part de « pour » demandée, à {n} votants comme d'habitude.",
|
||||
'settings.quorum.note': "Quorum : en dessous, les votants qui manquent comptent comme « non ». C'est celui du cran ; l'auteur d'un article peut en fixer un autre avant le premier vote.",
|
||||
'settings.required.version.one': 'Voix pour : à {n} votant, la participation de cette version.',
|
||||
'settings.required.version.other': 'Voix pour : à {n} votants, la participation de cette version.',
|
||||
'settings.required.usual.one': "Voix pour : à {n} votant, comme d'habitude.",
|
||||
'settings.required.usual.other': "Voix pour : à {n} votants, comme d'habitude.",
|
||||
'settings.reading': "À chaque cran, le groupe demande sa part de « pour » au-delà de C quand il vote comme d'habitude ; plus il y a de votants, plus elle se rapproche de M. Rien ne passe sous C voix pour.",
|
||||
'settings.reading': "À chaque cran, le groupe demande sa part de « pour » quand il vote comme d'habitude ; plus il y a de votants, plus elle se rapproche de M. Sous le quorum du cran, les votants qui manquent comptent comme « non ».",
|
||||
|
||||
'history.title': 'Corrections',
|
||||
'history.initial': 'texte initial',
|
||||
|
||||
+21
-3
@@ -4,17 +4,19 @@ export type LangMap = Partial<Record<Lang, string>>;
|
||||
export type Inertia = 1 | 2 | 3 | 4 | 5;
|
||||
export const INERTIA_LEVELS: Inertia[] = [1, 2, 3, 4, 5];
|
||||
|
||||
/** One level on the group's scale: M and G of the formula, s_k, and the *for* votes asked at `cast` votes. */
|
||||
/** One level on the group's scale: M and G of the formula, s_k, its quorum, and the *for* votes asked at `cast` votes. */
|
||||
export interface LevelInfo {
|
||||
level: Inertia;
|
||||
majorityPct: number;
|
||||
gradient: number;
|
||||
sharePct: number;
|
||||
/** Q — the quorum of the level (ADR 0013); from a version, its article's own if it has one. */
|
||||
quorum?: number;
|
||||
cast: number;
|
||||
required: number | null;
|
||||
}
|
||||
|
||||
/** p̃ — the usual turnout of the group, measured once a day (or the reference 2C/W). */
|
||||
/** p̃ — the usual turnout of the group, measured once a day (or the reference 2Q₀/W, Q₀ = `voteFloor`). */
|
||||
export interface ParticipationInfo {
|
||||
ratio: number;
|
||||
source: 'median' | 'reference';
|
||||
@@ -60,11 +62,17 @@ export interface Tally {
|
||||
|
||||
export interface Evaluation {
|
||||
cast: number;
|
||||
/** Q — the quorum it is judged on (ADR 0013): below it, the voters missing count as against. */
|
||||
quorum?: number;
|
||||
required: number;
|
||||
reached: boolean;
|
||||
missing: number;
|
||||
requiredRatio: number;
|
||||
wellRated: boolean;
|
||||
/** The total of *for* that passes with the *against* cast now. */
|
||||
yesNeeded?: number;
|
||||
/** The total of *against* that holds the *for* cast now in check (0: the quorum holds them). */
|
||||
noNeeded?: number;
|
||||
}
|
||||
|
||||
export interface Translation {
|
||||
@@ -204,6 +212,12 @@ export interface Unit {
|
||||
members?: string[];
|
||||
/** An order of fewer than two articles: nothing to decide, not shown. */
|
||||
moot?: boolean;
|
||||
/** Its own quorum, set by the author of its first version before the first vote; `null`: its level's (ADR 0013). */
|
||||
quorum?: number | null;
|
||||
/** The quorum of its level. */
|
||||
quorumDefault?: number;
|
||||
/** This session may set its quorum now (`PUT /units/{id}/quorum`). */
|
||||
canSetQuorum?: boolean;
|
||||
}
|
||||
|
||||
/** What a document decides besides its articles: its title, the order of each section. */
|
||||
@@ -213,7 +227,8 @@ export interface Modules {
|
||||
}
|
||||
|
||||
export interface Publication {
|
||||
state: 'none' | 'pending' | 'done';
|
||||
/** `suspended`: no article in force yet, the queue waits for the first text (ADR 0011). */
|
||||
state: 'none' | 'pending' | 'suspended' | 'done';
|
||||
configured: boolean;
|
||||
since?: string;
|
||||
doneAt?: string | null;
|
||||
@@ -233,7 +248,10 @@ export interface DocView {
|
||||
title: LangMap;
|
||||
summary: LangMap;
|
||||
sourceLang: Lang;
|
||||
/** Q₀ — the reference quorum (the C of before): the reference turnout is 2Q₀/W. */
|
||||
voteFloor: number;
|
||||
/** The quorum of each level, 1 to 5 (ADR 0013). */
|
||||
quorums?: number[];
|
||||
electorate: number;
|
||||
electorateSource: 'wot' | 'group';
|
||||
formula: { base: number };
|
||||
|
||||
@@ -14,6 +14,20 @@ describe('five levels of inertia', () => {
|
||||
expect(INERTIA_LEVELS.map((k) => en[`inertia.name.${k}`])).toEqual(['very low', 'low', 'medium', 'high', 'very high']);
|
||||
expect(INERTIA_LEVELS.map((k) => es[`inertia.name.${k}`])).toEqual(['muy baja', 'baja', 'media', 'alta', 'muy alta']);
|
||||
});
|
||||
it('the threshold is told with the quorum, no longer with C (ADR 0013)', () => {
|
||||
const told = [
|
||||
'deep.note', 'formula.reading', 'settings.reading', 'settings.share.note.one', 'settings.share.note.other',
|
||||
'settings.usual.reference.one', 'settings.usual.reference.other',
|
||||
] as const;
|
||||
for (const cat of [fr, en, es]) {
|
||||
for (const k of told) expect(cat[k], k).not.toMatch(/\bC\b/);
|
||||
expect(cat['deep.note']).not.toContain('{floor}');
|
||||
expect(cat['formula.Q']).toContain('{n}');
|
||||
expect(cat['settings.usual.reference.other']).toContain('{q}');
|
||||
}
|
||||
expect(fr['formula.reading']).toContain('les votants qui manquent comptent comme « non »');
|
||||
expect(fr['settings.quorum.note']).toContain('avant le premier vote');
|
||||
});
|
||||
it('a change of inertia is said to be voted at the level of the article, no longer one above', () => {
|
||||
for (const cat of [fr, en, es]) expect(cat['inertia.change.note']).toContain('{level}');
|
||||
expect(fr['inertia.change.note']).toContain("à l'inertie de l'article");
|
||||
|
||||
Reference in New Issue
Block a user