Skip to content

Commit 3e96d92

Browse files
aborrusoclaude
andcommitted
feat(site): card votazioni con atto votato — chi l'ha presentato, su cosa, doppio link
- MOZ/RIS n-nnnnn → identifier AIC → una sola query SPARQL VALUES risolve tutte le card: presentatore (gruppo) + incipit del 'premesso che' - link 'testo dell'atto' (aic.camera.it; DDL → scheda atto camera.it) accanto a 'scheda votazione' Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 62561b2 commit 3e96d92

2 files changed

Lines changed: 70 additions & 3 deletions

File tree

site/app.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,48 @@ function voteTitle(desc) {
540540
return (desc || "Votazione").replace(/^([A-Z]+)\b/, (m) => VOTE_KINDS[m] || m);
541541
}
542542

543+
/** L'atto votato: MOZ/RIS → scheda AIC; altrimenti DDL → scheda atto Camera. */
544+
function voteActRef(v) {
545+
const leg = ((v.legislature_uri || "").match(/repubblica_(\d+)/) || [])[1] || "19";
546+
const m = (v.description || "").match(/^(MOZ|RIS)\s+(\d+)-(\d+)\b/);
547+
if (m) {
548+
const id = `${m[2]}/${m[3].padStart(5, "0")}`;
549+
return { kind: "aic", id, url: `https://aic.camera.it/aic/scheda.html?core=aic&numero=${id}&ramo=CAMERA&leg=${leg}` };
550+
}
551+
if (v.bill_number) {
552+
return { kind: "bill", id: v.bill_number, url: `https://www.camera.it/leg${leg}/126?leg=${leg}&idDocumento=${v.bill_number}` };
553+
}
554+
return null;
555+
}
556+
557+
function titleCase(s) {
558+
return s.toLowerCase().replace(/(^|[\s'-])\p{L}/gu, (c) => c.toUpperCase());
559+
}
560+
561+
/** Una sola query SPARQL risolve tutti gli atti AIC delle card: chi li ha presentati e su cosa. */
562+
async function enrichVoteActs(leg) {
563+
const els = [...document.querySelectorAll("[data-aic]")];
564+
const ids = [...new Set(els.map((el) => el.dataset.aic))];
565+
if (!ids.length) return;
566+
const query = `PREFIX ocd: <http://dati.camera.it/ocd/> PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> `
567+
+ `SELECT DISTINCT ?id ?label (SUBSTR(STR(?d), 1, 700) AS ?excerpt) WHERE { `
568+
+ `?s a ocd:aic ; ocd:rif_leg <http://dati.camera.it/ocd/legislatura.rdf/repubblica_${leg}> ; dc:identifier ?id ; rdfs:label ?label . `
569+
+ `OPTIONAL { ?s dc:description ?d } VALUES ?id { ${ids.map((i) => `"${i}"`).join(" ")} } }`;
570+
const rows = await callTool("sparql", { query, endpoint: "camera" }, 12 * HOUR);
571+
const byId = new Map(rows.map((r) => [r.id, r]));
572+
for (const el of els) {
573+
const r = byId.get(el.dataset.aic);
574+
if (!r) continue;
575+
const lm = (r.label || "").match(/presentat[ao] da\s+([^(]+?)\s*\(([^)]+)\)/i);
576+
const who = lm ? `${titleCase(lm[1].trim().split(/\s+/)[0])} (${groupAcronym(lm[2])})` : "";
577+
const em = (r.excerpt || "").match(/premesso che:?\s*(?:\d+\)\s*)?(.{40,160})/);
578+
const cut = em ? em[1].slice(0, em[1].lastIndexOf(" ")).replace(/[,;:]$/, "") : "";
579+
const topic = cut ? `: «${cut}…»` : "";
580+
el.textContent = who ? `di ${who}${topic}` : "";
581+
if (el.textContent) gsap.from(el, { autoAlpha: 0, duration: 0.6 });
582+
}
583+
}
584+
543585
async function loadVotes() {
544586
const from = new Date(Date.now() - 60 * 86400e3).toISOString().slice(0, 10);
545587
const rows = await callTool("votes", { dateFrom: from, limit: 9 }, 1 * HOUR);
@@ -552,22 +594,31 @@ async function loadVotes() {
552594
for (const v of rows) {
553595
const present = Math.max(1, +v.present || (+v.in_favour + +v.against + +v.abstentions));
554596
const ok = v.approved === "true";
597+
const ref = voteActRef(v);
555598
const card = document.createElement("article");
556599
card.className = "vote-card";
557600
card.innerHTML = `
558601
<div class="vote-date"><span>${fmtDate(v.date)} · Camera</span><span class="vote-badge ${ok ? "ok" : "no"}">${ok ? "approvata" : "respinta"}</span></div>
559-
<h3 class="vote-title"><a href="${v.url}" target="_blank" rel="noopener">${voteTitle(v.description || v.title)}</a></h3>
602+
<h3 class="vote-title">${voteTitle(v.description || v.title)}</h3>
603+
${ref?.kind === "aic" ? `<div class="vote-atto" data-aic="${ref.id}"></div>` : ""}
560604
<div class="vote-bars">
561605
${[["f", "sì", v.in_favour], ["c", "no", v.against], ["a", "ast", v.abstentions]].map(([cls, lab, n]) => `
562606
<div class="vote-bar ${cls}">
563607
<span>${lab}</span>
564608
<span class="bar-track"><span class="bar-fill" data-w="${Math.round((+n / present) * 100)}"></span></span>
565609
<span class="bar-n">${n || 0}</span>
566610
</div>`).join("")}
611+
</div>
612+
<div class="vote-links mono">
613+
${ref ? `<a href="${ref.url}" target="_blank" rel="noopener">testo dell'atto ↗</a><span class="sep">·</span>` : ""}
614+
<a href="${v.url}" target="_blank" rel="noopener">scheda votazione ↗</a>
567615
</div>`;
568616
grid.appendChild(card);
569617
}
570618

619+
const leg = ((rows[0]?.legislature_uri || "").match(/repubblica_(\d+)/) || [])[1] || "19";
620+
enrichVoteActs(leg).catch((e) => console.warn("[italianparliament] enrich", e));
621+
571622
gsap.from(grid.children, {
572623
autoAlpha: 0, y: 26, stagger: 0.08, duration: 0.7, ease: "power2.out",
573624
scrollTrigger: { trigger: grid, start: "top 80%" },

site/style.css

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,24 @@ h2 {
304304
line-height: 1.4;
305305
flex: 1;
306306
}
307-
.vote-title a { border: none; color: inherit; }
308-
.vote-title a:hover { color: var(--gold-hi); }
307+
.vote-atto {
308+
font-size: 0.8rem;
309+
font-style: italic;
310+
color: var(--text-dim);
311+
line-height: 1.5;
312+
}
313+
.vote-atto:empty { display: none; }
314+
315+
.vote-links {
316+
display: flex;
317+
gap: 0.6rem;
318+
align-items: baseline;
319+
font-size: 0.66rem;
320+
letter-spacing: 0.05em;
321+
}
322+
.vote-links a { color: var(--gold-hi); border-bottom-color: transparent; }
323+
.vote-links a:hover { border-bottom-color: var(--gold); }
324+
.vote-links .sep { color: var(--text-dim); }
309325

310326
.vote-badge {
311327
font-family: var(--mono);

0 commit comments

Comments
 (0)