forked from yvv/decision
v3 : relecture de a576b68 — les propositions retirées sortent de la médiane, les réglages montrent la vraie participation
La médiane de participation ne compte plus les propositions retirées (invisibles, votes non repris). Quand l'échelle ramène la participation dans ses bornes, les réglages disent la médiane mesurée (ou deux fois C) et, à part, la valeur sur laquelle les seuils se calculent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,13 +38,15 @@ pub struct Measure {
|
||||
}
|
||||
|
||||
/// The numbers of voters of the group's proposals voted on since `since`.
|
||||
/// Drafts and withdrawn proposals stay out: neither is shown, and votes on a
|
||||
/// withdrawn proposal can no longer be taken back.
|
||||
pub async fn sample(state: &AppState, group: &str, since: &str) -> sqlx::Result<Vec<u32>> {
|
||||
let rows: Vec<i64> = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM votes v
|
||||
JOIN proposals p ON p.id = v.proposal_id
|
||||
JOIN units u ON u.id = p.unit_id
|
||||
JOIN documents d ON d.id = u.doc_id
|
||||
WHERE d.group_slug = ? AND p.status <> 'draft' AND v.revision = p.revision
|
||||
WHERE d.group_slug = ? AND p.status NOT IN ('draft', 'withdrawn') AND v.revision = p.revision
|
||||
GROUP BY v.proposal_id
|
||||
HAVING MAX(v.cast_at) >= ?",
|
||||
)
|
||||
|
||||
@@ -274,6 +274,42 @@ async fn the_usual_participation_is_the_median_of_five_voted_proposals() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_withdrawn_proposal_leaves_the_usual_participation() {
|
||||
// Relecture of a576b68: withdrawn proposals vanish from the document and their
|
||||
// votes can no longer be taken back, so they must not weigh on the median.
|
||||
let (mut first, state) = Client::new().await;
|
||||
let mut people = Vec::new();
|
||||
for name in ["W1", "W2", "W3", "W4"] {
|
||||
let mut c = first.other();
|
||||
c.login(name).await;
|
||||
people.push(c);
|
||||
}
|
||||
first.login("W1").await;
|
||||
let doc = first.doc("essai").await;
|
||||
let open: Vec<String> = ["I2", "T1", "E4", "E5", "E6"]
|
||||
.iter()
|
||||
.map(|code| unit(&doc, code)["proposals"][0]["id"].as_str().expect("open").to_owned())
|
||||
.collect();
|
||||
for p in &open {
|
||||
for c in people.iter_mut().take(2) {
|
||||
c.vote(p, "against").await;
|
||||
}
|
||||
}
|
||||
ld_server::participation::refresh(&state, true).await.expect("refresh");
|
||||
let p = first.doc("essai").await["doc"]["participation"].clone();
|
||||
assert_eq!((p["source"].as_str(), p["sample"].as_u64()), (Some("median"), Some(5)));
|
||||
|
||||
sqlx::query("UPDATE proposals SET status = 'withdrawn' WHERE id = ?")
|
||||
.bind(&open[0])
|
||||
.execute(&state.db)
|
||||
.await
|
||||
.expect("withdraw");
|
||||
ld_server::participation::refresh(&state, true).await.expect("refresh");
|
||||
let p = first.doc("essai").await["doc"]["participation"].clone();
|
||||
assert_eq!((p["source"].as_str(), p["sample"].as_u64()), (Some("reference"), Some(4)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The base as production holds it on 2026-10-30, then the new version
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user