forked from yvv/decision
v3 : modules d'ordre et de titre — moteur, données, serveur (L11)
L'ordre des articles de chaque section (cran 2) et le titre du document (cran 4) deviennent des modules : des unités à part, dont les versions sont des propositions votées comme les articles (dépliées, panier, chaîne). - moteur : ld-engine/src/order.rs (qui est dans l'ordre, ordre proposé, complété à la fin quand un article entre ou sort, places redistribuées, nouvel article en fin de section) ; pending : un ordre de moins de deux articles n'est jamais à voter ; - données : migration 0007 (units.kind, module_amendments) ; le seed déclare ses modules ; module_amend.rs les ajoute une fois, gardé et journalisé, aux documents nés sans eux, sans toucher articles ni votes ; - serveur : modules.rs, order.rs, doc_title.rs (portes pour proposer, entrée en vigueur : les articles changent de place, le document change de titre, en et es passent « à revoir ») ; publication des modules ; - reste mineur : la médiane compte les statuts open, in_force, applied, et eux seuls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -289,13 +289,15 @@ pub async fn status(state: &AppState) -> anyhow::Result<serde_json::Value> {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Markdown per language + JSON. Only the units in force, in order.
|
||||
/// Markdown per language + JSON. Only the articles in force, in order; the
|
||||
/// title of the document is its title in force (ADR 0009).
|
||||
pub async fn render(state: &AppState, st: &DocState) -> anyhow::Result<Vec<(String, String)>> {
|
||||
let titles = util::parse_lang(&st.doc.title);
|
||||
let src = st.doc.source_lang.clone();
|
||||
let modules = modules_json(state, st).await?;
|
||||
let stale_title = |lang: &str| modules["title"]["translations"][lang]["stale"].as_bool().unwrap_or(false);
|
||||
let in_force: Vec<_> = st
|
||||
.units
|
||||
.iter()
|
||||
.articles()
|
||||
.filter_map(|u| {
|
||||
let fid = u.in_force_id.as_ref()?;
|
||||
st.proposals.iter().find(|p| &p.id == fid).map(|p| (u, p))
|
||||
@@ -313,6 +315,9 @@ pub async fn render(state: &AppState, st: &DocState) -> anyhow::Result<Vec<(Stri
|
||||
st.doc.slug,
|
||||
if lang == src { "fait_foi: oui\n".to_owned() } else { "traduction: editoriale\n".to_owned() },
|
||||
);
|
||||
if lang != src && stale_title(lang) {
|
||||
md.push_str(&format!("\n<!-- {lang} : titre à revoir, le titre source a changé -->\n"));
|
||||
}
|
||||
for (_, p) in &in_force {
|
||||
let t = tr.get(&p.id).and_then(|m| m.get(lang));
|
||||
let (ut, ub, fallback) = match t {
|
||||
@@ -356,8 +361,61 @@ pub async fn render(state: &AppState, st: &DocState) -> anyhow::Result<Vec<(Stri
|
||||
let json = json!({
|
||||
"document": st.doc.slug, "group": st.group.slug, "title": titles, "sourceLang": src,
|
||||
"source": source_url, "updatedAt": updated, "electorate": st.electorate,
|
||||
"voteFloor": st.doc.vote_floor, "units": units,
|
||||
"voteFloor": st.doc.vote_floor, "units": units, "modules": modules,
|
||||
});
|
||||
files.push(("document.json".into(), format!("{}\n", serde_json::to_string_pretty(&json)?)));
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
/// The modules of the document as published: the title and the order of each
|
||||
/// section, each with its level, its state and its votes (ADR 0009).
|
||||
async fn modules_json(state: &AppState, st: &DocState) -> anyhow::Result<serde_json::Value> {
|
||||
use crate::modules::ModuleKind;
|
||||
let status = |p: &docs::ProposalRow| {
|
||||
if p.entered_at.is_some() {
|
||||
"adopted"
|
||||
} else if p.confirmed_at.is_some() {
|
||||
"confirmed"
|
||||
} else {
|
||||
"seed-minimal"
|
||||
}
|
||||
};
|
||||
let version = |u: &docs::UnitRow| u.in_force_id.as_ref().and_then(|id| st.proposals.iter().find(|p| &p.id == id));
|
||||
let title = match st.modules(ModuleKind::Title).first().and_then(|u| Some((*u, version(u)?))) {
|
||||
Some((u, p)) => {
|
||||
let tr = docs::translations_for(state, &[p]).await.map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||
let translations: serde_json::Map<String, serde_json::Value> = tr
|
||||
.get(&p.id)
|
||||
.map(|m| {
|
||||
m.iter().map(|(l, t)| (l.clone(), json!({ "title": t["title"], "stale": t["stale"] }))).collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let tally = st.tally(&p.id);
|
||||
json!({
|
||||
"inertia": crate::docs::inertia_of(u), "status": status(p), "enteredAt": p.entered_at,
|
||||
"votes": { "for": tally.for_votes, "against": tally.against }, "translations": translations,
|
||||
})
|
||||
}
|
||||
None => serde_json::Value::Null,
|
||||
};
|
||||
let orders: Vec<_> = st
|
||||
.modules(ModuleKind::Order)
|
||||
.into_iter()
|
||||
.filter_map(|u| {
|
||||
let p = version(u)?;
|
||||
let section = u.section.clone().unwrap_or_default();
|
||||
let codes: Vec<&str> = st
|
||||
.members(§ion)
|
||||
.iter()
|
||||
.filter_map(|id| st.units.iter().find(|x| &x.id == id).map(|x| x.code.as_str()))
|
||||
.collect();
|
||||
let tally = st.tally(&p.id);
|
||||
Some(json!({
|
||||
"section": section, "inertia": crate::docs::inertia_of(u), "status": status(p),
|
||||
"enteredAt": p.entered_at, "votes": { "for": tally.for_votes, "against": tally.against },
|
||||
"articles": codes,
|
||||
}))
|
||||
})
|
||||
.collect();
|
||||
Ok(json!({ "title": title, "orders": orders }))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user