Reveal.js deck + prototypes + speaker scripts for the talk 'Role of AI in the Analysis of Unstructured Clinical Databases' (AritmoLab — Policlinico San Donato, 2-3 October 2026). Lives on its own branch while in progress; not for main until ready.
229 lines
16 KiB
HTML
229 lines
16 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>PROTOTYPE · Content proposals — Role of AI in Unstructured Clinical Databases</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;0,9..144,700;1,9..144,400&family=Manrope:wght@400;600;700;800&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root {
|
||
--bordeaux: #cb333b; --bordeaux-tint: #f4dfe1; --bordeaux-deep: #8f1204;
|
||
--surface: #fdfcfc; --surface-alt: #f6f5f5; --surface-sunken: #ecebeb;
|
||
--ink: #2a2426; --ink-muted: #6b6568;
|
||
--line: #e3e0e1; --line-strong: #c9c5c6;
|
||
--sans: 'Manrope', system-ui, sans-serif; --display: 'Fraunces', Georgia, serif;
|
||
--mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
|
||
}
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body { background: var(--surface-sunken); font-family: var(--sans); padding: 0 0 80px; color: var(--ink); }
|
||
|
||
.sheet { max-width: 1280px; width: calc(100% - 32px); margin: 40px auto 0; background: var(--surface);
|
||
border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 1px 2px rgba(0,0,0,.03);
|
||
overflow: hidden; }
|
||
.head { flex: 0 0 104px; height: 104px; display: flex; align-items: center; justify-content: space-between;
|
||
padding: 0 40px; background: var(--surface); border-bottom: 1px solid var(--line); }
|
||
.head img { height: 80px; display: block; }
|
||
.head-org { font: 700 11.5px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-muted); }
|
||
.band { height: 46px; display: flex; align-items: center; justify-content: space-between;
|
||
padding: 0 40px; background: var(--bordeaux); color: var(--surface); }
|
||
.band .band-section { font: 700 13px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase; }
|
||
.band .tabs { display: flex; gap: 28px; }
|
||
.band .tabs a { font: 600 12px/1 var(--sans); letter-spacing: .1em; text-transform: uppercase;
|
||
color: rgba(255,255,255,.6); padding: 17px 0 13px; border-bottom: 3px solid transparent;
|
||
text-decoration: none; cursor: pointer; }
|
||
.band .tabs a.on { color: #fff; border-bottom-color: #fff; }
|
||
|
||
.lede { padding: 40px 48px 30px; border-bottom: 1px solid var(--line); }
|
||
.kicker { font: 700 12px/1 var(--sans); letter-spacing: .16em; text-transform: uppercase;
|
||
color: var(--bordeaux); margin-bottom: 14px; }
|
||
.lede h1 { font: 600 34px/1.15 var(--display); letter-spacing: -.01em; }
|
||
.lede p { margin-top: 14px; font: 400 15.5px/1.55 var(--sans); color: var(--ink-muted); max-width: 88ch; }
|
||
.meta { display: flex; gap: 12px; margin-top: 20px; flex-wrap: wrap; }
|
||
.meta span { font: 700 11.5px/1 var(--sans); letter-spacing: .08em; text-transform: uppercase;
|
||
color: var(--ink-muted); border: 1px solid var(--line-strong); border-radius: 9999px; padding: 8px 14px; }
|
||
.meta span b { color: var(--bordeaux); }
|
||
|
||
.story { padding: 30px 48px 44px; display: flex; flex-direction: column; gap: 12px; }
|
||
.sl { display: flex; align-items: center; gap: 20px; padding: 16px 20px;
|
||
border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
|
||
.sl.sp { background: var(--bordeaux-tint); border-color: #e7c3c6; }
|
||
.sl.demo { border-style: dashed; border-color: var(--line-strong); }
|
||
.sl .no { flex: 0 0 40px; font: 600 26px/1 var(--display); color: var(--bordeaux); }
|
||
.sl .tx { flex: 1; min-width: 0; }
|
||
.sl .tx b { display: block; font: 700 16px/1.3 var(--sans); }
|
||
.sl .tx span { display: block; margin-top: 4px; font: 400 13.5px/1.45 var(--sans); color: var(--ink-muted); }
|
||
.sl .side { flex: 0 0 auto; display: flex; align-items: center; gap: 10px; }
|
||
.chip { font: 700 10px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase;
|
||
color: var(--ink-muted); border: 1px solid var(--line-strong); border-radius: 9999px; padding: 5px 10px; }
|
||
.chip.speaker { color: #fff; background: var(--bordeaux); border-color: var(--bordeaux); }
|
||
.sl.sp .chip.speaker { background: var(--bordeaux-deep); border-color: var(--bordeaux-deep); }
|
||
.chip.tbd { color: var(--bordeaux-deep); border-color: var(--bordeaux); background: #fff; }
|
||
|
||
/* compare view */
|
||
.cmp { display: grid; grid-template-columns: 44px repeat(3, 1fr); gap: 4px 10px; padding: 26px 48px 44px; align-items: stretch; }
|
||
.cmp .h { font: 800 10.5px/1.4 var(--mono); letter-spacing: .1em; text-transform: uppercase;
|
||
color: var(--bordeaux-deep); padding: 6px 6px 8px; border-bottom: 2px solid var(--bordeaux); }
|
||
.cmp .h span { display: block; color: var(--ink-muted); font-weight: 400; text-transform: none; letter-spacing: 0; }
|
||
.cmp .no { font: 600 15px/1.2 var(--display); color: var(--bordeaux); padding: 8px 2px 6px;
|
||
border-bottom: 1px solid var(--line); }
|
||
.cmp .cell { padding: 7px 9px; border-bottom: 1px solid var(--line); border-radius: 6px; }
|
||
.cmp .cell b { display: block; font: 700 12.5px/1.3 var(--sans); color: var(--ink); }
|
||
.cmp .cell .s { display: block; margin-top: 3px; font: 600 9.5px/1 var(--mono); letter-spacing: .05em;
|
||
color: var(--ink-muted); text-transform: uppercase; }
|
||
.cmp .cell.same { border-bottom-color: transparent; }
|
||
.cmp .cell.same b { color: var(--ink-muted); font-weight: 600; }
|
||
.cmp .cell.diff { background: var(--bordeaux-tint); border-bottom-color: #e7c3c6; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="sheet">
|
||
<header class="head">
|
||
<img src="logo.png" alt="Policlinico San Donato">
|
||
<span class="head-org">AritmoLab · Policlinico San Donato</span>
|
||
</header>
|
||
<nav class="band">
|
||
<span class="band-section">Content proposals · light layout</span>
|
||
<span class="tabs" id="tabs"></span>
|
||
</nav>
|
||
<section class="lede" id="lede"></section>
|
||
<section class="story" id="story"></section>
|
||
</div>
|
||
|
||
<script>
|
||
const proposals = {
|
||
1: {
|
||
tab: 'A · Three roles',
|
||
name: 'A — The three roles of AI',
|
||
principle: 'Organized by what the AI does: it reads the text, it co-engineers the platform, it answers questions. Three acts, one per role, with the clinical text-analysis block as the centerpiece.',
|
||
fit: 'Best for a mixed technical audience · the band navigation maps 1:1 to the acts',
|
||
risk: 'Engineering-first framing — the patient story stays implicit',
|
||
meta: [['Slides', '14'], ['Talk time', '≈ 12.3 min'], ['Sara’s block', '4 slides'], ['Demo', 'star schema + ThothII']],
|
||
slides: [
|
||
[01,'Title','Title, speakers, event, venue & date (clean — no diagram)','title','MP','40s'],
|
||
[02,'The project at a glance','Project flow: 3 sources + future → staging → integration 🧠 → star schema → datamarts 🧠 → AritmoLab (DWH + portal)','architecture','MP','75s'],
|
||
[03,'The problem — truth locked in free text','One real sentence from a discharge letter: diagnosis, history, test outcome — all prose','concept','MP','60s'],
|
||
[04,'What the text miner reads','58,438 letters · 73,389 pathologies · 10,908 drug-challenge tests · 2,307 Brugada','stats','SP','60s'],
|
||
[05,'The hard problems of clinical NLP','Negation, family attribution, bilingual IT/EN, abbreviations, clause splitting','concept','SP','75s'],
|
||
[06,'Building the clinical ontology','12 TIER1 + 5 TIER2 categories, clinical precedence ordering, semver patterns','concept','SP','75s'],
|
||
[07,'Trust the text: validation & human review','≥95% / ≥85% gates on 100 manual reviews, the v1.3.1 false-positive fix','concept','SP','75s'],
|
||
[08,'AI as co-engineer','19,764 lines of mapping YAML drafted by LLM agents, reviewed by humans, versioned in git','artifact','MP','70s'],
|
||
[09,'Genetic data — many sources, one patient','9 Kokoro genetic tables + genetic visits + file imports; genotype ↔ phenotype bridges','architecture','MP','60s','tbd'],
|
||
[10,'ThothII: ask the warehouse in plain English','NL → SQL over the star schema, generates the final datamarts','concept','MP','75s'],
|
||
[11,'From datamarts to prediction','Curated cohorts feed predictive statistics and ML models','concept','MP','60s'],
|
||
[12,'Live demo','Star schema, Superset dashboards, ThothII answering a new question','divider','MP','~2 min live'],
|
||
[13,'Lessons learned','What we would do again, what we changed','concept','MP','60s'],
|
||
[14,'Thank you + questions','Substack deep-dive videos pointer','closing','MP','25s'],
|
||
],
|
||
},
|
||
2: {
|
||
tab: 'B · One patient',
|
||
name: 'B — One patient’s journey',
|
||
principle: 'The same content, organized around a single (composite, anonymized) Brugada patient: from his admission letter to his cohort, his genetics, his datamart, a prediction. The platform is told through him.',
|
||
fit: 'Best when clinicians outnumber engineers · the most memorable arc',
|
||
risk: 'Platform and engineering content gets compressed; the two speakers must interlock tightly. Needs a clear “composite case” disclaimer.',
|
||
meta: [['Slides', '13'], ['Talk time', '≈ 12.4 min'], ['Sara’s block', '4 slides'], ['Demo', 'merged into the journey']],
|
||
slides: [
|
||
[01,'Title','Title, speakers, event, venue & date (clean — no diagram)','title','MP','40s'],
|
||
[02,'The project at a glance','Project flow: 3 sources + future → staging → integration 🧠 → star schema → datamarts 🧠 → AritmoLab (DWH + portal)','architecture','MP','75s'],
|
||
[03,'Meet the patient','Syncope, ECG with ST elevation, positive flecainide test — one letter tells it all','concept','MP','60s'],
|
||
[04,'Where his story lives','Hospital systems keep the truth in free-text columns','concept','MP','60s'],
|
||
[05,'Reading his letter','The hard NLP problems: negation, family, bilingual, clauses','concept','SP','75s'],
|
||
[06,'His diagnosis, in our language','The clinical ontology: categories, precedence, versions','concept','SP','75s'],
|
||
[07,'His test, his cohort','Positive drug challenge → one of 2,307 confirmed Brugada patients','stats','SP','60s'],
|
||
[08,'Trusting what we read','Validation protocol, manual reviews, clinician dashboards','concept','SP','60s'],
|
||
[09,'His genetics','Many sources, one patient: reconciling genetic data','architecture','MP','60s','tbd'],
|
||
[10,'His data, queryable','Star schema + dashboards','divider','MP','~2 min live'],
|
||
[11,'Ask in plain English','ThothII over the warehouse: datamarts on demand','concept','MP','75s'],
|
||
[12,'From his cohort to prediction','Cohorts → predictive statistics → ML models','concept','MP','60s'],
|
||
[13,'Lessons, questions, thanks','Substack deep-dives pointer','closing','MP','60s'],
|
||
],
|
||
},
|
||
3: {
|
||
tab: 'C · Three questions',
|
||
name: 'C — From questions to answers',
|
||
principle: 'Start from three research questions the clinic could not answer: ablation effectiveness in Brugada, gene–phenotype associations, predicting progression. Each AI role appears only as the enabler that unlocks one answer.',
|
||
fit: 'Best when the room wants results first · strongest opening hook (real numbers on slide 6)',
|
||
risk: 'The NLP/ontology depth shrinks — Sara’s block goes from 4 to 2–3 slides; engineering becomes invisible unless narrated',
|
||
meta: [['Slides', '13'], ['Talk time', '≈ 12.4 min'], ['Sara’s block', '2–3 slides'], ['Demo', 'a brand-new question, live']],
|
||
slides: [
|
||
[01,'Title','Title, speakers, event, venue & date (clean — no diagram)','title','MP','40s'],
|
||
[02,'The project at a glance','Project flow: 3 sources + future → staging → integration 🧠 → star schema → datamarts 🧠 → AritmoLab (DWH + portal)','architecture','MP','60s'],
|
||
[03,'Three questions we could not answer','Q1 ablation effectiveness in Brugada · Q2 gene–phenotype associations · Q3 predicting progression','concept','MP','75s'],
|
||
[04,'Where the answers hide','The answers exist — written in free text, scattered across systems','concept','MP','60s'],
|
||
[05,'Q1 — reading 58,438 letters','Clinical NLP: problems + ontology, compressed','concept','SP','90s'],
|
||
[06,'Q1 answered — and validated','1,244 Brugada patients with effective ablation; ≥95% gate on 100 manual reviews','stats','SP','60s'],
|
||
[07,'Q2 — reconciling genetics','Many sources, one patient','architecture','MP','60s','tbd'],
|
||
[08,'Q2 answered — genotype ↔ phenotype','Links between variants and clinical pictures','concept','MP','60s'],
|
||
[09,'Q3 — from datamarts to prediction','ThothII builds the cohorts; statistics and ML do the rest','concept','MP','75s'],
|
||
[10,'Live demo — a new question','ThothII answers a question from the audience','divider','MP','~2 min live'],
|
||
[11,'What we learned building it','Lessons learned','concept','MP','60s'],
|
||
[12,'Trust by design — recap','Deterministic, versioned, human-validated','concept','MP','45s'],
|
||
[13,'Thank you + questions','Substack deep-dive videos pointer','closing','MP','25s'],
|
||
],
|
||
},
|
||
};
|
||
|
||
const c = ['0','1','2','3'].includes(new URLSearchParams(location.search).get('c')) ? new URLSearchParams(location.search).get('c') : '1';
|
||
|
||
document.getElementById('tabs').innerHTML =
|
||
`<a href="?c=0" class="${c === '0' ? 'on' : ''}">⚡ Compare</a>` +
|
||
Object.entries(proposals).map(([k, v]) => `<a href="?c=${k}" class="${k === c ? 'on' : ''}">${v.tab}</a>`).join('');
|
||
|
||
if (c === '0') {
|
||
document.getElementById('lede').innerHTML = `
|
||
<div class="kicker">Compare · three story organizations, one consolidated layout</div>
|
||
<h1>Same skeleton, different story</h1>
|
||
<p>All three proposals share the same skeleton: clean opening → <b>project flow as slide 02</b> →
|
||
the clinical text-analysis block → engineering → live demo → thanks. What changes is
|
||
<b>what comes right after the flow</b> and the angle of the narration.
|
||
Pink rows = that proposal tells something different in that position; grey rows are identical.</p>
|
||
<div class="meta">
|
||
<span><b>A</b> · by AI role · 14 slides</span>
|
||
<span><b>B</b> · by one patient · 13 slides</span>
|
||
<span><b>C</b> · by research questions · 13 slides</span>
|
||
</div>`;
|
||
const names = {
|
||
1: 'A · Three roles<span>classic technical arc</span>',
|
||
2: 'B · One patient<span>narrative arc, clinician-friendly</span>',
|
||
3: 'C · Three questions<span>results-first, strongest hook</span>',
|
||
};
|
||
const maxLen = Math.max(...Object.values(proposals).map(p => p.slides.length));
|
||
let rows = `<div class="h"></div>` + [1, 2, 3].map(k => `<div class="h">${names[k]}</div>`).join('');
|
||
for (let i = 0; i < maxLen; i++) {
|
||
const cells = [1, 2, 3].map(k => proposals[k].slides[i] || null);
|
||
const titles = cells.map(s => (s ? s[1] : null));
|
||
const same = titles[0] !== null && titles.every(t => t === titles[0]);
|
||
rows += `<div class="no">${String(i + 1).padStart(2, '0')}</div>` + cells.map(s =>
|
||
s ? `<div class="cell ${same ? 'same' : 'diff'}"><b>${s[1]}</b><span class="s">${s[4]} · ${s[5]}</span></div>`
|
||
: `<div class="cell"><span class="s">—</span></div>`).join('');
|
||
}
|
||
document.getElementById('story').innerHTML = `<div class="cmp">${rows}</div>`;
|
||
} else {
|
||
const p = proposals[c];
|
||
document.getElementById('lede').innerHTML = `
|
||
<div class="kicker">Proposal ${c} of 3 · same consolidated light layout</div>
|
||
<h1>${p.name}</h1>
|
||
<p>${p.principle}</p>
|
||
<div class="meta">
|
||
${p.meta.map(([k, v]) => `<span>${k}: <b>${v}</b></span>`).join('')}
|
||
</div>
|
||
<p><strong>Fit:</strong> ${p.fit}. <strong>Watch out:</strong> ${p.risk}.</p>`;
|
||
document.getElementById('story').innerHTML = p.slides.map(([no, title, desc, master, sp, t, flag]) => `
|
||
<div class="sl ${sp === 'SP' ? 'sp' : ''} ${master === 'divider' ? 'demo' : ''}">
|
||
<span class="no">${String(no).padStart(2, '0')}</span>
|
||
<span class="tx"><b>${title}</b><span>${desc}</span></span>
|
||
<span class="side">
|
||
${flag === 'tbd' ? '<span class="chip tbd">wording TBD</span>' : ''}
|
||
<span class="chip">${master}</span>
|
||
<span class="chip speaker">${sp}</span>
|
||
<span class="chip">${t}</span>
|
||
</span>
|
||
</div>`).join('');
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|