feat: viewer — affichage qualité CPAM, traçabilité décisions DP/DAS, VetoReport et bio
- CPAM : badge quality_tier (A/B/C), bandeau requires_review, warnings catégorisés, force probante dossier - DP/DAS : code suggestion barré → code final si modifié, ligne grisée si ruled_out, badges décision + règles - DAS : badge needs_info avec détails, raison ruled_out sous la ligne - VetoReport : section contestabilité avec verdict, barre score/100, tableau issues HARD/MEDIUM/LOW - Biologie : badge Suspect avec tooltip, valeurs écartées en details pliable - Nouveau filtre Jinja2 decision_badge, import _assess_dossier_strength (pas de duplication) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -131,9 +131,14 @@
|
||||
{% if dossier.controles_cpam %}
|
||||
<div class="card section" style="border-left:4px solid #f59e0b;">
|
||||
<h3 style="color:#b45309;">Contrôle CPAM ({{ dossier.controles_cpam|length }})</h3>
|
||||
{% if dossier_strength and dossier_strength.is_weak %}
|
||||
<div style="background:#fff7ed;border:1px solid #fed7aa;padding:0.5rem 0.75rem;border-radius:4px;margin-bottom:0.75rem;font-size:0.85rem;color:#9a3412;">
|
||||
Dossier à preuves limitées (score {{ dossier_strength.score }}/10) — manque : {{ dossier_strength.missing|join(', ') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for ctrl in dossier.controles_cpam %}
|
||||
<div style="margin-bottom:1.5rem;{% if not loop.last %}border-bottom:1px solid #e2e8f0;padding-bottom:1rem;{% endif %}">
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.5rem;">
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.5rem;flex-wrap:wrap;">
|
||||
<strong>OGC {{ ctrl.numero_ogc }} — {{ ctrl.titre }}</strong>
|
||||
{% if 'retient' in ctrl.decision_ucr|lower %}
|
||||
<span class="badge" style="background:#d1fae5;color:#065f46;">{{ ctrl.decision_ucr }}</span>
|
||||
@@ -142,6 +147,13 @@
|
||||
{% else %}
|
||||
<span class="badge" style="background:#e0e7ff;color:#3730a3;">{{ ctrl.decision_ucr }}</span>
|
||||
{% endif %}
|
||||
{% if ctrl.quality_tier == 'A' %}
|
||||
<span class="badge" style="background:#2ecc71;color:#fff;font-weight:700;">Qualité A</span>
|
||||
{% elif ctrl.quality_tier == 'B' %}
|
||||
<span class="badge" style="background:#f39c12;color:#fff;font-weight:700;">Qualité B</span>
|
||||
{% elif ctrl.quality_tier == 'C' %}
|
||||
<span class="badge" style="background:#e74c3c;color:#fff;font-weight:700;">Qualité C</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Argument CPAM #}
|
||||
@@ -165,6 +177,13 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Bandeau revue manuelle si Tier C #}
|
||||
{% if ctrl.requires_review %}
|
||||
<div style="background:#fee2e2;border:1px solid #fca5a5;padding:0.5rem 0.75rem;border-radius:4px;margin-bottom:0.75rem;font-size:0.85rem;color:#991b1b;">
|
||||
⚠ Revue manuelle requise — la contre-argumentation contient des incohérences détectées
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Contre-argumentation structurée ou fallback texte brut #}
|
||||
{% if ctrl.response_data %}
|
||||
<div style="margin-bottom:0.75rem;">
|
||||
@@ -261,6 +280,22 @@
|
||||
{% endfor %}
|
||||
</details>
|
||||
{% endif %}
|
||||
|
||||
{# Avertissements qualité #}
|
||||
{% if ctrl.quality_warnings %}
|
||||
<details style="margin-top:0.5rem;">
|
||||
<summary style="font-size:0.8rem;color:#9333ea;">Avertissements qualité ({{ ctrl.quality_warnings|length }})</summary>
|
||||
<ul style="margin:0.25rem 0;padding-left:1.2rem;">
|
||||
{% for w in ctrl.quality_warnings %}
|
||||
{% if w.startswith('[CRITIQUE]') %}
|
||||
<li style="color:#dc2626;font-size:0.8rem;">{{ w }}</li>
|
||||
{% else %}
|
||||
<li style="color:#d97706;font-size:0.8rem;">{{ w }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -282,17 +317,75 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# ---- Contestabilité (VetoReport) ---- #}
|
||||
{% if dossier.veto_report %}
|
||||
{% set vr = dossier.veto_report %}
|
||||
{% if vr.verdict == 'PASS' %}
|
||||
{% set vr_color = '#22c55e' %}
|
||||
{% elif vr.verdict == 'NEED_INFO' %}
|
||||
{% set vr_color = '#f59e0b' %}
|
||||
{% else %}
|
||||
{% set vr_color = '#ef4444' %}
|
||||
{% endif %}
|
||||
<div class="card section" style="border-left:4px solid {{ vr_color }};">
|
||||
<h3>Contestabilité du dossier</h3>
|
||||
<div style="display:flex;align-items:center;gap:1rem;">
|
||||
{% if vr.verdict == 'PASS' %}
|
||||
<span class="badge" style="background:#d1fae5;color:#065f46;font-weight:700;">PASS</span>
|
||||
{% elif vr.verdict == 'NEED_INFO' %}
|
||||
<span class="badge" style="background:#fef3c7;color:#92400e;font-weight:700;">NEED_INFO</span>
|
||||
{% else %}
|
||||
<span class="badge" style="background:#fee2e2;color:#dc2626;font-weight:700;">FAIL</span>
|
||||
{% endif %}
|
||||
<div style="flex:1;height:8px;background:#e2e8f0;border-radius:4px;">
|
||||
<div style="width:{{ vr.score_contestabilite }}%;height:100%;background:{{ vr_color }};border-radius:4px;"></div>
|
||||
</div>
|
||||
<span style="font-weight:600;">{{ vr.score_contestabilite }}/100</span>
|
||||
</div>
|
||||
{% if vr.issues %}
|
||||
<details style="margin-top:0.5rem;">
|
||||
<summary style="font-size:0.8rem;color:#64748b;">Problèmes détectés ({{ vr.issues|length }})</summary>
|
||||
<table style="margin-top:0.25rem;">
|
||||
<thead><tr><th>Veto</th><th>Sévérité</th><th>Localisation</th><th>Message</th></tr></thead>
|
||||
<tbody>
|
||||
{% for issue in vr.issues %}
|
||||
<tr>
|
||||
<td><code style="font-size:0.75rem;">{{ issue.veto }}</code></td>
|
||||
<td>
|
||||
{% if issue.severity == 'HARD' %}<span class="badge" style="background:#fee2e2;color:#dc2626;">HARD</span>
|
||||
{% elif issue.severity == 'MEDIUM' %}<span class="badge" style="background:#fef3c7;color:#92400e;">MEDIUM</span>
|
||||
{% else %}<span class="badge" style="background:#f0fdf4;color:#166534;">LOW</span>{% endif %}
|
||||
</td>
|
||||
<td style="font-size:0.75rem;color:#64748b;">{{ issue.where }}</td>
|
||||
<td style="font-size:0.8rem;">{{ issue.message }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# ---- Diagnostic principal ---- #}
|
||||
{% if dossier.diagnostic_principal %}
|
||||
{% set dp = dossier.diagnostic_principal %}
|
||||
<div class="card section">
|
||||
<div class="card section"{% if dp.status == 'ruled_out' %} style="opacity:0.5;"{% endif %}>
|
||||
<h3>Diagnostic principal</h3>
|
||||
<div style="font-size:0.95rem;margin-bottom:0.5rem;">
|
||||
{{ dp.texte }}
|
||||
{% if dp.status == 'ruled_out' %}<span style="text-decoration:line-through;">{{ dp.texte }}</span>{% else %}{{ dp.texte }}{% endif %}
|
||||
{% if dp.source_page %}<button class="src-btn" data-texte="{{ dp.texte|e }}" data-excerpt="{{ dp.source_excerpt|default('',true)|e }}" data-page="{{ dp.source_page }}">p.{{ dp.source_page }}</button>{% endif %}
|
||||
</div>
|
||||
{% if dp.cim10_suggestion %}
|
||||
<span class="badge" style="background:#dbeafe;color:#1d4ed8;font-size:0.85rem;">{{ dp.cim10_suggestion }}</span>
|
||||
{% if dp.cim10_final and dp.cim10_final != dp.cim10_suggestion %}
|
||||
<span style="text-decoration:line-through;color:#94a3b8;font-size:0.85rem;">{{ dp.cim10_suggestion }}</span>
|
||||
<span style="color:#64748b;">→</span>
|
||||
<span class="badge" style="background:#dbeafe;color:#1d4ed8;font-size:0.85rem;">{{ dp.cim10_final }}</span>
|
||||
{% elif dp.status == 'ruled_out' %}
|
||||
<span style="text-decoration:line-through;color:#94a3b8;font-size:0.85rem;">{{ dp.cim10_suggestion }}</span>
|
||||
{% else %}
|
||||
<span class="badge" style="background:#dbeafe;color:#1d4ed8;font-size:0.85rem;">{{ dp.cim10_suggestion }}</span>
|
||||
{% endif %}
|
||||
{{ dp.cim10_confidence | confidence_badge }}
|
||||
{% if dp.niveau_cma and dp.niveau_cma > 1 %}
|
||||
{{ dp.niveau_cma | cma_level_badge }}
|
||||
@@ -300,6 +393,18 @@
|
||||
<span class="badge" style="background:#fee2e2;color:#dc2626;font-size:0.75rem;">CMA</span>
|
||||
{% endif %}
|
||||
{{ dp.niveau_severite | severity_badge }}
|
||||
{% if dp.cim10_decision and dp.cim10_decision.action != 'KEEP' %}
|
||||
{{ dp.cim10_decision | decision_badge }}
|
||||
{% for rule in dp.cim10_decision.applied_rules %}
|
||||
<span class="badge" style="background:#f1f5f9;color:#64748b;font-size:0.65rem;">{{ rule }}</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if dp.status == 'ruled_out' and dp.ruled_out_reason %}
|
||||
<div style="font-size:0.75rem;color:#dc2626;margin-top:0.25rem;">{{ dp.ruled_out_reason }}</div>
|
||||
{% endif %}
|
||||
{% if dp.cim10_decision and dp.cim10_decision.action != 'KEEP' and dp.cim10_decision.reason %}
|
||||
<div style="font-size:0.75rem;color:#64748b;margin-top:0.25rem;">{{ dp.cim10_decision.reason }}</div>
|
||||
{% endif %}
|
||||
{% if dp.justification %}
|
||||
<div style="margin-top:0.5rem;font-size:0.8rem;color:#475569;">{{ dp.justification }}</div>
|
||||
@@ -340,9 +445,43 @@
|
||||
<thead><tr><th>Texte</th><th>CIM-10</th><th>Confiance</th><th>CMA</th><th>Source</th><th>Justification</th></tr></thead>
|
||||
<tbody>
|
||||
{% for das in dossier.diagnostics_associes %}
|
||||
<tr>
|
||||
<tr{% if das.status == 'ruled_out' %} style="opacity:0.5;text-decoration:line-through;"{% endif %}>
|
||||
<td>{{ das.texte }}</td>
|
||||
<td>{% if das.cim10_suggestion %}<span class="badge" style="background:#dbeafe;color:#1d4ed8;">{{ das.cim10_suggestion }}</span>{% endif %}</td>
|
||||
<td>
|
||||
{% if das.cim10_suggestion %}
|
||||
{% if das.cim10_final and das.cim10_final != das.cim10_suggestion %}
|
||||
<span style="text-decoration:line-through;color:#94a3b8;font-size:0.8rem;">{{ das.cim10_suggestion }}</span>
|
||||
<span style="color:#64748b;">→</span>
|
||||
<span class="badge" style="background:#dbeafe;color:#1d4ed8;">{{ das.cim10_final }}</span>
|
||||
{% elif das.status == 'ruled_out' %}
|
||||
<span style="color:#94a3b8;">{{ das.cim10_suggestion }}</span>
|
||||
{% else %}
|
||||
<span class="badge" style="background:#dbeafe;color:#1d4ed8;">{{ das.cim10_suggestion }}</span>
|
||||
{% endif %}
|
||||
{% if das.cim10_decision and das.cim10_decision.action != 'KEEP' %}
|
||||
<div style="margin-top:0.2rem;">
|
||||
{{ das.cim10_decision | decision_badge }}
|
||||
{% for rule in das.cim10_decision.applied_rules %}
|
||||
<span class="badge" style="background:#f1f5f9;color:#64748b;font-size:0.65rem;">{{ rule }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if das.status == 'needs_info' %}
|
||||
<div style="margin-top:0.2rem;">
|
||||
<span class="badge" style="background:#fff7ed;color:#c2410c;font-size:0.7rem;">Info requise</span>
|
||||
{% if das.cim10_decision and das.cim10_decision.needs_info %}
|
||||
<details style="margin-top:0.15rem;"><summary style="font-size:0.7rem;color:#c2410c;cursor:pointer;">détails</summary>
|
||||
<ul style="margin:0.1rem 0;padding-left:1rem;font-size:0.7rem;color:#9a3412;">
|
||||
{% for ni in das.cim10_decision.needs_info %}
|
||||
<li>{{ ni }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ das.cim10_confidence | confidence_badge }}</td>
|
||||
<td>
|
||||
{% if das.niveau_cma and das.niveau_cma > 1 %}
|
||||
@@ -374,6 +513,20 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if das.status == 'ruled_out' and das.ruled_out_reason %}
|
||||
<tr>
|
||||
<td colspan="6" style="padding:0 0.75rem 0.3rem;">
|
||||
<div style="font-size:0.75rem;color:#dc2626;">{{ das.ruled_out_reason }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if das.cim10_decision and das.cim10_decision.action != 'KEEP' and das.cim10_decision.reason and das.status != 'ruled_out' %}
|
||||
<tr>
|
||||
<td colspan="6" style="padding:0 0.75rem 0.3rem;">
|
||||
<div style="font-size:0.75rem;color:#64748b;">{{ das.cim10_decision.reason }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if das.raisonnement %}
|
||||
<tr>
|
||||
<td colspan="6" style="padding:0 0.75rem 0.5rem;">
|
||||
@@ -446,15 +599,36 @@
|
||||
<thead><tr><th>Test</th><th>Valeur</th><th>Anomalie</th><th>Source</th></tr></thead>
|
||||
<tbody>
|
||||
{% for b in dossier.biologie_cle %}
|
||||
<tr{% if b.anomalie %} class="anomalie"{% endif %}>
|
||||
<tr{% if b.quality == 'suspect' %} style="background:#fffbeb;"{% elif b.anomalie %} class="anomalie"{% endif %}>
|
||||
<td>{{ b.test }}</td>
|
||||
<td>{{ b.valeur or '' }}</td>
|
||||
<td>{% if b.anomalie %}<span class="badge" style="background:#fee2e2;color:#dc2626;">Oui</span>{% else %}—{% endif %}</td>
|
||||
<td>
|
||||
{% if b.quality == 'suspect' %}
|
||||
<span class="badge" style="background:#fef3c7;color:#92400e;" title="{{ b.discard_reason or '' }}">⚠ Suspect</span>
|
||||
{% elif b.anomalie %}
|
||||
<span class="badge" style="background:#fee2e2;color:#dc2626;">Oui</span>
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{% if b.source_page %}<button class="src-btn" data-texte="{{ b.test|e }}" data-excerpt="{{ b.source_excerpt|default('',true)|e }}" data-page="{{ b.source_page }}">p.{{ b.source_page }}</button>{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if dossier.biologie_discarded %}
|
||||
<details style="margin-top:0.5rem;">
|
||||
<summary style="font-size:0.8rem;color:#d97706;">Valeurs écartées ({{ dossier.biologie_discarded|length }})</summary>
|
||||
<table style="margin-top:0.25rem;">
|
||||
<thead><tr><th>Test</th><th>Valeur</th><th>Raison</th></tr></thead>
|
||||
<tbody>
|
||||
{% for b in dossier.biologie_discarded %}
|
||||
<tr style="opacity:0.6;"><td>{{ b.test or '' }}</td><td>{{ b.valeur or '' }}</td><td>{{ b.discard_reason or '—' }}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user