<?php
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Extension\CoreExtension;
use Twig\Extension\SandboxExtension;
use Twig\Markup;
use Twig\Sandbox\SecurityError;
use Twig\Sandbox\SecurityNotAllowedTagError;
use Twig\Sandbox\SecurityNotAllowedFilterError;
use Twig\Sandbox\SecurityNotAllowedFunctionError;
use Twig\Source;
use Twig\Template;
use Twig\TemplateWrapper;
/* classroom/show.html.twig */
class __TwigTemplate_0c035c967b72fb6156d097bb36f556af extends Template
{
private Source $source;
/**
* @var array<string, Template>
*/
private array $macros = [];
public function __construct(Environment $env)
{
parent::__construct($env);
$this->source = $this->getSourceContext();
$this->blocks = [
'stylesheets' => [$this, 'block_stylesheets'],
'content' => [$this, 'block_content'],
'javascripts' => [$this, 'block_javascripts'],
];
}
protected function doGetParent(array $context): bool|string|Template|TemplateWrapper
{
// line 1
return "layout/backEndLayout.html.twig";
}
protected function doDisplay(array $context, array $blocks = []): iterable
{
$macros = $this->macros;
$__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
$__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template", "classroom/show.html.twig"));
$__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
$__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template", "classroom/show.html.twig"));
$this->parent = $this->load("layout/backEndLayout.html.twig", 1);
yield from $this->parent->unwrap()->yield($context, array_merge($this->blocks, $blocks));
$__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
$__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
}
// line 3
/**
* @return iterable<null|scalar|\Stringable>
*/
public function block_stylesheets(array $context, array $blocks = []): iterable
{
$macros = $this->macros;
$__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
$__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "stylesheets"));
$__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
$__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "stylesheets"));
// line 4
yield from $this->yieldParentBlock("stylesheets", $context, $blocks);
yield "
<style>
/* ══════════════════════════════════════════════════════
TOKENS
══════════════════════════════════════════════════════ */
:root {
--bg : #f0f4f8; --s1 : #ffffff; --s2 : #e8edf3;
--bdr : rgba(0,0,0,.09); --bdr-hi: rgba(0,0,0,.18);
--blue : #185fa5; --rose : #a32d2d; --amber: #854f0b;
--teal : #0f6e56; --em : #3b6d11; --vi : #534ab7;
--txt : #1a2535; --dim : #4a5568; --muted: #7a8899;
--r: 14px; --font: 'DM Sans','Segoe UI',sans-serif;
}
/* ── Layout ──────────────────────────────────────────── */
.cr-page { font-family:var(--font); color:var(--txt); }
.cr-page * { box-sizing:border-box; }
/* ── Entête classe ───────────────────────────────────── */
.cr-head {
background:var(--s1); border:1px solid var(--bdr);
border-radius:var(--r); padding:20px 24px; margin-bottom:20px;
display:flex; align-items:center; justify-content:space-between;
flex-wrap:wrap; gap:14px;
}
.cr-head__name { font-size:22px; font-weight:700; letter-spacing:-.02em; }
.cr-head__meta { font-size:13px; color:var(--dim); }
.cr-head__badges { display:flex; gap:8px; flex-wrap:wrap; }
.badge-pill {
display:inline-flex; align-items:center; gap:5px; padding:5px 12px;
border-radius:20px; font-size:12px; font-weight:500; border:1px solid var(--bdr);
}
.bp-blue { background:rgba(59,130,246,.15); color:var(--blue); }
.bp-amber { background:rgba(245,158,11,.15); color:var(--amber); }
.bp-teal { background:rgba(20,184,166,.15); color:var(--teal); }
/* ── Actions ─────────────────────────────────────────── */
.cr-actions {
background:var(--s1); border:1px solid var(--bdr); border-radius:var(--r);
padding:14px 18px; margin-bottom:20px; display:flex; flex-wrap:wrap; gap:8px;
}
.btn-cr {
display:inline-flex; align-items:center; gap:6px; padding:7px 14px;
border-radius:8px; font-size:12.5px; font-weight:500; text-decoration:none;
border:1px solid var(--bdr); cursor:pointer; transition:background .18s,transform .12s;
color:var(--txt); background:var(--s2);
}
.btn-cr:hover { transform:translateY(-1px); border-color:var(--bdr-hi); color:var(--txt); }
.btn-cr-blue { background:rgba(59,130,246,.15); color:var(--blue); border-color:rgba(59,130,246,.3); }
.btn-cr-amber { background:rgba(245,158,11,.15); color:var(--amber); border-color:rgba(245,158,11,.3); }
.btn-cr-teal { background:rgba(20,184,166,.15); color:var(--teal); border-color:rgba(20,184,166,.3); }
.btn-cr-rose { background:rgba(244,63,94,.15); color:var(--rose); border-color:rgba(244,63,94,.3); }
.btn-cr-vi { background:rgba(139,92,246,.15); color:var(--vi); border-color:rgba(139,92,246,.3); }
.btn-cr-em { background:rgba(16,185,129,.15); color:var(--em); border-color:rgba(16,185,129,.3); }
/* ── Tables ──────────────────────────────────────────── */
.cr-table-wrap {
background:var(--s1); border:1px solid var(--bdr); border-radius:var(--r);
overflow:hidden; margin-bottom:20px;
}
.cr-table-title {
padding:14px 18px 12px; border-bottom:1px solid var(--bdr);
font-size:14px; font-weight:600; display:flex; align-items:center; gap:8px;
}
.cr-table-title .dot { width:7px;height:7px;border-radius:50%; }
.d-bl{background:var(--blue);box-shadow:0 0 5px var(--blue);}
.d-am{background:var(--amber);box-shadow:0 0 5px var(--amber);}
table.cr-tbl { width:100%; border-collapse:collapse; font-size:13px; }
table.cr-tbl th { padding:10px 14px; color:var(--dim); font-weight:500; font-size:11.5px; text-transform:uppercase; letter-spacing:.05em; border-bottom:1px solid var(--bdr); background:var(--s2); }
table.cr-tbl td { padding:9px 14px; border-bottom:1px solid rgba(0,0,0,.05); }
table.cr-tbl tr:last-child td { border-bottom:none; }
table.cr-tbl tr:hover td { background:rgba(0,0,0,.025); }
.tag-exam { display:inline-block; background:rgba(244,63,94,.15); color:var(--rose); padding:2px 8px; border-radius:4px; font-size:11px; }
/* ── Grille graphiques ───────────────────────────────── */
.chart-grid { display:grid; gap:16px; margin-bottom:16px; }
.cg-2 { grid-template-columns:1fr 1fr; }
.cg-1 { grid-template-columns:1fr; }
@media(max-width:900px){ .cg-2 { grid-template-columns:1fr; } }
/* ── Carte graphique ─────────────────────────────────── */
.cc {
background:var(--s1); border:1px solid var(--bdr); border-radius:var(--r);
overflow:hidden; transition:border-color .2s; animation:cIn .4s ease both;
}
.cc:hover { border-color:var(--bdr-hi); }
.cc__hd {
display:flex; align-items:center; justify-content:space-between;
padding:13px 18px 11px; border-bottom:1px solid var(--bdr);
}
.cc__lbl { display:flex; align-items:center; gap:8px; font-size:13px; font-weight:600; }
.cc__dot { width:7px;height:7px;border-radius:50%;flex-shrink:0; }
.d-tl{background:var(--teal);box-shadow:0 0 5px var(--teal);}
.d-rs{background:var(--rose);box-shadow:0 0 5px var(--rose);}
.d-vi{background:var(--vi);box-shadow:0 0 5px var(--vi);}
.d-em{background:var(--em);box-shadow:0 0 5px var(--em);}
.cc__sub { font-size:11px; color:var(--muted); }
.cc__bd { padding:16px 18px 18px; }
/* ── Heatmap ─────────────────────────────────────────── */
.heatmap { overflow-x:auto; }
.heatmap table { width:100%; border-collapse:collapse; font-size:12px; }
.heatmap th { padding:7px 10px; color:var(--dim); font-size:11px; text-transform:uppercase; letter-spacing:.05em; text-align:center; font-weight:500; }
.heatmap th.course-th { text-align:left; min-width:140px; }
.heatmap td { padding:5px 8px; text-align:center; border:1px solid rgba(0,0,0,.05); font-weight:500; font-size:12.5px; border-radius:4px; transition:opacity .2s; }
.heatmap td:hover { opacity:.8; }
.heatmap td.course-td { text-align:left; color:var(--txt); font-weight:400; font-size:12px; padding-left:4px; background:transparent !important; border:none; }
.hm-null { background:rgba(0,0,0,.05); color:var(--muted); }
.hm-red { background:rgba(163,45,45,.15); color:#7f1d1d; }
.hm-yellow { background:rgba(133,79,11,.15); color:#451a03; }
.hm-green { background:rgba(15,110,86,.15); color:#064e3b; }
@keyframes cIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.cc:nth-child(1){animation-delay:.04s}.cc:nth-child(2){animation-delay:.1s}
#c-evol { max-height:220px; width:100%!important; }
#c-success{ max-height:260px; width:100%!important; }
#c-radar { max-height:300px; width:100%!important; }
#c-mentions{ max-height:220px; max-width:220px; margin:auto; display:block; }
</style>
";
$__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
$__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
yield from [];
}
// line 126
/**
* @return iterable<null|scalar|\Stringable>
*/
public function block_content(array $context, array $blocks = []): iterable
{
$macros = $this->macros;
$__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
$__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "content"));
$__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
$__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "content"));
// line 127
yield "<div class=\"cr-page p-3\">
";
// line 130
yield " <div class=\"cr-head\">
<div>
<div class=\"cr-head__name\">";
// line 132
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 132, $this->source); })()), "name", [], "any", false, false, false, 132), "html", null, true);
yield "</div>
<div class=\"cr-head__meta\">
Niveau ";
// line 134
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 134, $this->source); })()), "level", [], "any", false, false, false, 134), "html", null, true);
yield "
";
// line 135
if ((($tmp = (isset($context["mainteacher"]) || array_key_exists("mainteacher", $context) ? $context["mainteacher"] : (function () { throw new RuntimeError('Variable "mainteacher" does not exist.', 135, $this->source); })())) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
yield " · Titulaire : <strong>";
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, (isset($context["mainteacher"]) || array_key_exists("mainteacher", $context) ? $context["mainteacher"] : (function () { throw new RuntimeError('Variable "mainteacher" does not exist.', 135, $this->source); })()), "fullName", [], "any", false, false, false, 135), "html", null, true);
yield "</strong>";
}
// line 136
yield " </div>
</div>
<div class=\"cr-head__badges\">
<span class=\"badge-pill bp-blue\"><i class=\"fa fa-users\"></i> ";
// line 139
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(Twig\Extension\CoreExtension::length($this->env->getCharset(), (isset($context["studentEnrolled"]) || array_key_exists("studentEnrolled", $context) ? $context["studentEnrolled"] : (function () { throw new RuntimeError('Variable "studentEnrolled" does not exist.', 139, $this->source); })())), "html", null, true);
yield " élèves</span>
";
// line 140
if ((($tmp = CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 140, $this->source); })()), "apc", [], "any", false, false, false, 140)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
// line 141
yield " <span class=\"badge-pill bp-amber\"><i class=\"fa fa-graduation-cap\"></i> Classe d'examen</span>
";
}
// line 143
yield " </div>
</div>
";
// line 147
yield " <div class=\"cr-actions\">
<a class=\"btn-cr btn-cr-blue\" href=\"";
// line 148
yield $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_classrooms");
yield "\"><i class=\"fa fa-list\"></i> Liste</a>
";
// line 149
if ((($tmp = $this->extensions['Symfony\Bridge\Twig\Extension\SecurityExtension']->isGranted("ROLE_ADMIN")) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
// line 150
yield " <a class=\"btn-cr btn-cr-blue\" href=\"";
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_classrooms_edit", ["id" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 150, $this->source); })()), "id", [], "any", false, false, false, 150)]), "html", null, true);
yield "\"><i class=\"fa fa-edit\"></i> Modifier</a>
<a class=\"btn-cr btn-cr-amber\" data-toggle=\"modal\" data-target=\"#form_modal_courses\" data-action=\"";
// line 151
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_classrooms_recapitulatif_seq", ["id" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 151, $this->source); })()), "id", [], "any", false, false, false, 151)]), "html", null, true);
yield "\"><i class=\"fa fa-file-pdf-o\"></i> Recap Seq</a>
<a class=\"btn-cr btn-cr-amber\" href=\"";
// line 152
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_classrooms_reportcards_seq", ["id" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 152, $this->source); })()), "id", [], "any", false, false, false, 152)]), "html", null, true);
yield "\"><i class=\"fa fa-file\"></i> Bull Seq</a>
<a class=\"btn-cr btn-cr-teal\" data-toggle=\"modal\" data-target=\"#form_modal_reportcard_params\" data-action=\"";
// line 153
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_classrooms_reportcards_trim_2024", ["id" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 153, $this->source); })()), "id", [], "any", false, false, false, 153)]), "html", null, true);
yield "\"><i class=\"fa fa-file\"></i> Bull Trim</a>
<a class=\"btn-cr btn-cr-vi\" data-toggle=\"modal\" data-target=\"#form_modal_reportcard_params\" data-action=\"";
// line 154
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_class_reportcards_year_2024", ["id" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 154, $this->source); })()), "id", [], "any", false, false, false, 154)]), "html", null, true);
yield "\"><i class=\"fa fa-file-pdf-o\"></i> Bull Ann</a>
<a class=\"btn-cr btn-cr-rose\" data-toggle=\"modal\" data-target=\"#form_modal_courses\" data-action=\"";
// line 155
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_classrooms_recapitulatif_trim", ["id" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 155, $this->source); })()), "id", [], "any", false, false, false, 155)]), "html", null, true);
yield "\"><i class=\"fa fa-file-pdf-o\"></i> Recap Trim</a>
<a class=\"btn-cr btn-cr-em\" data-toggle=\"modal\" data-target=\"#form_modal_courses\" data-action=\"";
// line 156
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_classrooms_recapitulatif_ann_excel", ["id" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 156, $this->source); })()), "id", [], "any", false, false, false, 156)]), "html", null, true);
yield "\"><i class=\"fa fa-file-excel-o\"></i> Recap Ann</a>
";
}
// line 158
yield " </div>
";
// line 161
yield " <div class=\"cg-2 chart-grid\">
<div class=\"cr-table-wrap\">
<div class=\"cr-table-title\"><span class=\"dot d-bl\"></span>Élèves inscrits</div>
<table class=\"cr-tbl\">
<thead><tr><th>Matricule</th><th>Nom</th><th>Prénom</th>";
// line 166
if ((($tmp = $this->extensions['Symfony\Bridge\Twig\Extension\SecurityExtension']->isGranted("ROLE_ADMIN")) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
yield "<th>Action</th>";
}
yield "</tr></thead>
<tbody>
";
// line 168
$context["effectif"] = 0;
// line 169
yield " ";
$context['_parent'] = $context;
$context['_seq'] = CoreExtension::ensureTraversable((isset($context["studentEnrolled"]) || array_key_exists("studentEnrolled", $context) ? $context["studentEnrolled"] : (function () { throw new RuntimeError('Variable "studentEnrolled" does not exist.', 169, $this->source); })()));
foreach ($context['_seq'] as $context["_key"] => $context["std"]) {
// line 170
yield " <tr>
<td><a href=\"";
// line 171
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_students_show", ["id" => CoreExtension::getAttribute($this->env, $this->source, $context["std"], "id", [], "any", false, false, false, 171)]), "html", null, true);
yield "\" style=\"color:var(--blue)\">";
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["std"], "matricule", [], "any", false, false, false, 171), "html", null, true);
yield "</a></td>
<td>";
// line 172
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["std"], "lastname", [], "any", false, false, false, 172), "html", null, true);
yield "</td>
<td>";
// line 173
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["std"], "firstname", [], "any", false, false, false, 173), "html", null, true);
yield "</td>
";
// line 174
if ((($tmp = $this->extensions['Symfony\Bridge\Twig\Extension\SecurityExtension']->isGranted("ROLE_ADMIN")) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
// line 175
yield " <td>
<a class=\"btn-cr btn-cr-rose\" style=\"padding:3px 8px;font-size:11px\" href=\"";
// line 176
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_students_unregister", ["id" => CoreExtension::getAttribute($this->env, $this->source, $context["std"], "id", [], "any", false, false, false, 176), "room_id" => CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 176, $this->source); })()), "id", [], "any", false, false, false, 176)]), "html", null, true);
yield "\"><i class=\"fa fa-ban\"></i></a>
<a class=\"btn-cr\" style=\"padding:3px 8px;font-size:11px\" href=\"";
// line 177
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_students_edit", ["id" => CoreExtension::getAttribute($this->env, $this->source, $context["std"], "id", [], "any", false, false, false, 177)]), "html", null, true);
yield "\"><i class=\"fa fa-pencil-square-o\"></i></a>
</td>
";
}
// line 180
yield " </tr>
";
// line 181
$context["effectif"] = ((isset($context["effectif"]) || array_key_exists("effectif", $context) ? $context["effectif"] : (function () { throw new RuntimeError('Variable "effectif" does not exist.', 181, $this->source); })()) + 1);
// line 182
yield " ";
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_key'], $context['std'], $context['_parent']);
$context = array_intersect_key($context, $_parent) + $_parent;
// line 183
yield " <tr><td colspan=\"2\" style=\"color:var(--dim);font-size:11.5px\">Effectif total</td><td style=\"font-weight:700;color:var(--blue)\">";
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["effectif"]) || array_key_exists("effectif", $context) ? $context["effectif"] : (function () { throw new RuntimeError('Variable "effectif" does not exist.', 183, $this->source); })()), "html", null, true);
yield "</td>";
if ((($tmp = $this->extensions['Symfony\Bridge\Twig\Extension\SecurityExtension']->isGranted("ROLE_ADMIN")) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
yield "<td></td>";
}
yield "</tr>
</tbody>
</table>
</div>
<div class=\"cr-table-wrap\">
<div class=\"cr-table-title\"><span class=\"dot d-am\"></span>Matières programmées</div>
<table class=\"cr-tbl\">
<thead><tr><th>Module</th><th>Code</th><th>Intitulé</th><th>Coef</th><th>Enseignant</th></tr></thead>
<tbody>
";
// line 193
$context["totalCoef"] = 0;
// line 194
yield " ";
$context['_parent'] = $context;
$context['_seq'] = CoreExtension::ensureTraversable((isset($context["modules"]) || array_key_exists("modules", $context) ? $context["modules"] : (function () { throw new RuntimeError('Variable "modules" does not exist.', 194, $this->source); })()));
foreach ($context['_seq'] as $context["_key"] => $context["module"]) {
// line 195
yield " ";
$context['_parent'] = $context;
$context['_seq'] = CoreExtension::ensureTraversable(CoreExtension::getAttribute($this->env, $this->source, $context["module"], "courses", [], "any", false, false, false, 195));
foreach ($context['_seq'] as $context["_key"] => $context["course"]) {
// line 196
yield " <tr>
<td style=\"color:var(--dim);font-size:11.5px\">";
// line 197
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["module"], "name", [], "any", false, false, false, 197), "html", null, true);
yield "</td>
<td><a href=\"";
// line 198
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_courses_show", ["id" => CoreExtension::getAttribute($this->env, $this->source, $context["course"], "id", [], "any", false, false, false, 198)]), "html", null, true);
yield "\" style=\"color:var(--blue)\">";
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["course"], "code", [], "any", false, false, false, 198), "html", null, true);
yield "</a></td>
<td>";
// line 199
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["course"], "wording", [], "any", false, false, false, 199), "html", null, true);
yield "</td>
<td style=\"font-weight:600\">";
// line 200
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["course"], "coefficient", [], "any", false, false, false, 200), "html", null, true);
yield "</td>
<td>
";
// line 202
if (CoreExtension::getAttribute($this->env, $this->source, ($context["attributions"] ?? null), CoreExtension::getAttribute($this->env, $this->source, $context["course"], "id", [], "any", false, false, false, 202), [], "array", true, true, false, 202)) {
// line 203
yield " ";
if ((($tmp = $this->extensions['Symfony\Bridge\Twig\Extension\SecurityExtension']->isGranted("ROLE_ADMIN")) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
// line 204
yield " <a href=\"";
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape($this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_attributions_edit", ["id" => CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["attributions"]) || array_key_exists("attributions", $context) ? $context["attributions"] : (function () { throw new RuntimeError('Variable "attributions" does not exist.', 204, $this->source); })()), CoreExtension::getAttribute($this->env, $this->source, $context["course"], "id", [], "any", false, false, false, 204), [], "array", false, false, false, 204), "id", [], "any", false, false, false, 204)]), "html", null, true);
yield "\" style=\"color:var(--teal)\">";
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["attributions"]) || array_key_exists("attributions", $context) ? $context["attributions"] : (function () { throw new RuntimeError('Variable "attributions" does not exist.', 204, $this->source); })()), CoreExtension::getAttribute($this->env, $this->source, $context["course"], "id", [], "any", false, false, false, 204), [], "array", false, false, false, 204), "teacher", [], "any", false, false, false, 204), "fullName", [], "any", false, false, false, 204), "html", null, true);
yield "</a>
";
} else {
// line 205
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, CoreExtension::getAttribute($this->env, $this->source, (isset($context["attributions"]) || array_key_exists("attributions", $context) ? $context["attributions"] : (function () { throw new RuntimeError('Variable "attributions" does not exist.', 205, $this->source); })()), CoreExtension::getAttribute($this->env, $this->source, $context["course"], "id", [], "any", false, false, false, 205), [], "array", false, false, false, 205), "teacher", [], "any", false, false, false, 205), "fullName", [], "any", false, false, false, 205), "html", null, true);
}
// line 206
yield " ";
} else {
// line 207
yield " ";
if ((($tmp = $this->extensions['Symfony\Bridge\Twig\Extension\SecurityExtension']->isGranted("ROLE_ADMIN")) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
yield "<a href=\"";
yield $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getPath("admin_attributions_new");
yield "\" style=\"color:var(--rose)\">Non attribué</a>";
}
// line 208
yield " ";
}
// line 209
yield " </td>
</tr>
";
// line 211
$context["totalCoef"] = ((isset($context["totalCoef"]) || array_key_exists("totalCoef", $context) ? $context["totalCoef"] : (function () { throw new RuntimeError('Variable "totalCoef" does not exist.', 211, $this->source); })()) + CoreExtension::getAttribute($this->env, $this->source, $context["course"], "coefficient", [], "any", false, false, false, 211));
// line 212
yield " ";
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_key'], $context['course'], $context['_parent']);
$context = array_intersect_key($context, $_parent) + $_parent;
// line 213
yield " ";
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_key'], $context['module'], $context['_parent']);
$context = array_intersect_key($context, $_parent) + $_parent;
// line 214
yield " <tr><td colspan=\"3\" style=\"color:var(--dim);font-size:11.5px\">Total coefficients</td><td style=\"font-weight:700;color:var(--amber)\" colspan=\"2\">";
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["totalCoef"]) || array_key_exists("totalCoef", $context) ? $context["totalCoef"] : (function () { throw new RuntimeError('Variable "totalCoef" does not exist.', 214, $this->source); })()), "html", null, true);
yield "</td></tr>
</tbody>
</table>
</div>
</div>
";
// line 224
yield "
";
// line 229
yield " <div class=\"cc\" style=\"margin-bottom:16px\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-am\"></span>Performances séquentielles — toutes matières</div>
<span class=\"cc__sub\">rouge <10 · jaune 10–13 · vert ≥14 · gris = pas d'évaluation enregistrée</span>
</div>
<div class=\"cc__bd heatmap\">
<table id=\"heatmap-table\">
<thead>
<tr>
<th class=\"course-th\">Matière</th>
";
// line 240
yield " </tr>
</thead>
<tbody id=\"heatmap-body\"></tbody>
</table>
</div>
</div>
";
// line 248
yield " <div class=\"chart-grid cg-2\">
<div class=\"cc\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-tl\"></span>Évolution de la moyenne générale</div>
<span class=\"cc__sub\">séquence par séquence · ligne rouge = seuil 10</span>
</div>
<div class=\"cc__bd\"><canvas id=\"c-evol\"></canvas></div>
</div>
<div class=\"cc\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-rs\"></span>Réussite vs Échec — ";
// line 260
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["activeSeqLabel"]) || array_key_exists("activeSeqLabel", $context) ? $context["activeSeqLabel"] : (function () { throw new RuntimeError('Variable "activeSeqLabel" does not exist.', 260, $this->source); })()), "html", null, true);
yield "</div>
<span class=\"cc__sub\">séquence active · avec absents</span>
</div>
<div class=\"cc__bd\"><canvas id=\"c-success\"></canvas></div>
</div>
</div>
";
// line 272
yield " <div class=\"chart-grid ";
yield (((($tmp = CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 272, $this->source); })()), "apc", [], "any", false, false, false, 272)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) ? ("cg-2") : ("cg-1"));
yield "\">
<div class=\"cc\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-vi\"></span>Profil trimestriel de la classe</div>
<span class=\"cc__sub\">radar par matière · T1, T2, T3</span>
</div>
<div class=\"cc__bd\"><canvas id=\"c-radar\"></canvas></div>
</div>
";
// line 282
if ((($tmp = CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 282, $this->source); })()), "apc", [], "any", false, false, false, 282)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
// line 283
yield " <div class=\"cc\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-em\"></span>Résultats examens officiels</div>
<span class=\"cc__sub\">répartition des mentions</span>
</div>
<div class=\"cc__bd\" style=\"display:flex;align-items:center;justify-content:center\">
<canvas id=\"c-mentions\"></canvas>
</div>
</div>
";
}
// line 293
yield "
</div>
</div>";
// line 297
yield "
";
// line 301
yield "<div class=\"modal fade\" id=\"form_modal_courses\" tabindex=\"-1\" role=\"dialog\">
<div class=\"modal-dialog modal-dialog-centered\">
<div class=\"modal-content\" style=\"background:#ffffff;color:#1a2535;border:1px solid rgba(0,0,0,.12)\">
<div class=\"modal-header border-bottom-0\">
<h5 class=\"modal-title\">Choisir les cours du récapitulatif</h5>
<button type=\"button\" class=\"close\" data-dismiss=\"modal\" style=\"color:var(--txt)\"><span>×</span></button>
</div>
<form action=\"#\" method=\"post\">
<div class=\"modal-body\">
";
// line 310
$context['_parent'] = $context;
$context['_seq'] = CoreExtension::ensureTraversable((isset($context["modules"]) || array_key_exists("modules", $context) ? $context["modules"] : (function () { throw new RuntimeError('Variable "modules" does not exist.', 310, $this->source); })()));
foreach ($context['_seq'] as $context["_key"] => $context["module"]) {
$context['_parent'] = $context;
$context['_seq'] = CoreExtension::ensureTraversable(CoreExtension::getAttribute($this->env, $this->source, $context["module"], "courses", [], "any", false, false, false, 310));
foreach ($context['_seq'] as $context["_key"] => $context["course"]) {
// line 311
yield " <li class=\"list-group-item\" style=\"background:transparent;border-color:var(--bdr);color:var(--txt)\">
<input class=\"form-check-input me-1\" type=\"checkbox\" checked name=\"selected_courses[]\" value=\"";
// line 312
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["course"], "id", [], "any", false, false, false, 312), "html", null, true);
yield "\">
";
// line 313
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["course"], "wording", [], "any", false, false, false, 313), "html", null, true);
yield "
</li>
";
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_key'], $context['course'], $context['_parent']);
$context = array_intersect_key($context, $_parent) + $_parent;
}
$_parent = $context['_parent'];
unset($context['_seq'], $context['_key'], $context['module'], $context['_parent']);
$context = array_intersect_key($context, $_parent) + $_parent;
// line 316
yield " </div>
<div class=\"modal-footer border-top-0 d-flex justify-content-center\">
<button type=\"submit\" class=\"btn-cr btn-cr-em\">Générer</button>
</div>
</form>
</div>
</div>
</div>
<div class=\"modal fade\" id=\"form_modal_reportcard_params\" tabindex=\"-1\" role=\"dialog\">
<div class=\"modal-dialog modal-dialog-centered\">
<div class=\"modal-content\" style=\"background:#ffffff;color:#1a2535;border:1px solid rgba(0,0,0,.12)\">
<div class=\"modal-header border-bottom-0\">
<h5 class=\"modal-title\">Paramètres du bulletin</h5>
<button type=\"button\" class=\"close\" data-dismiss=\"modal\" style=\"color:var(--txt)\"><span>×</span></button>
</div>
<form method=\"post\">
<div class=\"modal-body\">
<div class=\"form-check mb-2\"><input class=\"form-check-input\" type=\"checkbox\" name=\"copyright\" id=\"copyright\"><label class=\"form-check-label\" for=\"copyright\">Copyright en pied de page</label></div>
<div class=\"form-check\"><input class=\"form-check-input\" type=\"checkbox\" name=\"reverse\" id=\"reverse\"><label class=\"form-check-label\" for=\"reverse\">Sens inverse</label></div>
</div>
<div class=\"modal-footer border-top-0 d-flex justify-content-center\">
<button type=\"submit\" class=\"btn-cr btn-cr-blue\">Générer</button>
</div>
</form>
</div>
</div>
</div>
";
$__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
$__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
yield from [];
}
// line 347
/**
* @return iterable<null|scalar|\Stringable>
*/
public function block_javascripts(array $context, array $blocks = []): iterable
{
$macros = $this->macros;
$__internal_5a27a8ba21ca79b61932376b2fa922d2 = $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
$__internal_5a27a8ba21ca79b61932376b2fa922d2->enter($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "javascripts"));
$__internal_6f47bbe9983af81f1e7450e9a3e3768f = $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
$__internal_6f47bbe9983af81f1e7450e9a3e3768f->enter($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block", "javascripts"));
// line 348
yield from $this->yieldParentBlock("javascripts", $context, $blocks);
yield "
<script>
(function(){
'use strict';
/* ── Données depuis le controller ─────────────────────── */
const courseLabels = ";
// line 354
yield (isset($context["courseLabels"]) || array_key_exists("courseLabels", $context) ? $context["courseLabels"] : (function () { throw new RuntimeError('Variable "courseLabels" does not exist.', 354, $this->source); })());
yield ";
const seqLabels = ";
// line 355
yield (isset($context["seqLabels"]) || array_key_exists("seqLabels", $context) ? $context["seqLabels"] : (function () { throw new RuntimeError('Variable "seqLabels" does not exist.', 355, $this->source); })());
yield ";
const seqIds = ";
// line 356
yield (isset($context["seqIds"]) || array_key_exists("seqIds", $context) ? $context["seqIds"] : (function () { throw new RuntimeError('Variable "seqIds" does not exist.', 356, $this->source); })());
yield ";
const heatmapData = ";
// line 357
yield (isset($context["heatmapData"]) || array_key_exists("heatmapData", $context) ? $context["heatmapData"] : (function () { throw new RuntimeError('Variable "heatmapData" does not exist.', 357, $this->source); })());
yield ";
const generalAvgBySeq = ";
// line 358
yield (isset($context["generalAvgBySeq"]) || array_key_exists("generalAvgBySeq", $context) ? $context["generalAvgBySeq"] : (function () { throw new RuntimeError('Variable "generalAvgBySeq" does not exist.', 358, $this->source); })());
yield ";
const successFailData = ";
// line 359
yield (isset($context["successFailData"]) || array_key_exists("successFailData", $context) ? $context["successFailData"] : (function () { throw new RuntimeError('Variable "successFailData" does not exist.', 359, $this->source); })());
yield ";
const quaterProfiles = ";
// line 360
yield (isset($context["quaterProfiles"]) || array_key_exists("quaterProfiles", $context) ? $context["quaterProfiles"] : (function () { throw new RuntimeError('Variable "quaterProfiles" does not exist.', 360, $this->source); })());
yield ";
";
// line 361
if ((($tmp = CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 361, $this->source); })()), "apc", [], "any", false, false, false, 361)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
// line 362
yield "const mentionCategories = ";
yield (isset($context["mentionCategories"]) || array_key_exists("mentionCategories", $context) ? $context["mentionCategories"] : (function () { throw new RuntimeError('Variable "mentionCategories" does not exist.', 362, $this->source); })());
yield ";
const mentionCountCategories = ";
// line 363
yield (isset($context["mentionCountCategories"]) || array_key_exists("mentionCountCategories", $context) ? $context["mentionCountCategories"] : (function () { throw new RuntimeError('Variable "mentionCountCategories" does not exist.', 363, $this->source); })());
yield ";
";
}
// line 365
yield "
/* ── Palette & defaults ───────────────────────────────── */
const C = {
blue:'#185fa5', rose:'#a32d2d', amber:'#854f0b',
teal:'#0f6e56', vi:'#534ab7', em:'#3b6d11', orange:'#9a4f0b',
grid:'rgba(0,0,0,.07)', bg:'#ffffff', txt:'#1a2535', dim:'#4a5568'
};
const PALQ = ['rgba(24,95,165,.35)','rgba(15,110,86,.35)','rgba(83,74,183,.35)'];
const PALQ_BD = ['#185fa5', '#0f6e56', '#534ab7'];
const PAL = ['#3b82f6','#f59e0b','#f43f5e','#10b981','#8b5cf6','#14b8a6','#f97316','#84cc16'];
Chart.defaults.color = C.dim;
Chart.defaults.font.family = \"'DM Sans','Segoe UI',sans-serif\";
Chart.defaults.font.size = 12;
const TIP = {backgroundColor:'#ffffff',borderColor:'rgba(0,0,0,.12)',borderWidth:1,titleColor:C.txt,bodyColor:C.dim,padding:12,cornerRadius:10};
const AX = (o={}) => ({grid:{color:C.grid,drawBorder:false},ticks:{color:C.dim},border:{display:false},...o});
/* ══════════════════════════════════════════════════════
1. HEATMAP — table HTML colorée cours × séquences
Résout le problème de départ : chaque cellule est
indépendante. null = gris \"pas d'éval enregistrée\".
══════════════════════════════════════════════════════ */
(function buildHeatmap(){
const thead = document.querySelector('#heatmap-table thead tr');
const tbody = document.getElementById('heatmap-body');
// En-têtes séquences
seqLabels.forEach(lbl => {
const th = document.createElement('th');
th.textContent = lbl;
thead.appendChild(th);
});
// Colonne moyenne
const thAvg = document.createElement('th');
thAvg.textContent = 'Moy.';
thead.appendChild(thAvg);
// Lignes de données
heatmapData.forEach(row => {
const tr = document.createElement('tr');
// Cellule nom de la matière
const tdName = document.createElement('td');
tdName.className = 'course-td';
tdName.textContent = row.course;
tr.appendChild(tdName);
let sum = 0, cnt = 0;
// Cellules par séquence
seqIds.forEach(sid => {
const val = row['s' + sid];
const td = document.createElement('td');
if (val === null || val === undefined) {
td.className = 'hm-null';
td.textContent = '—';
} else {
td.className = val < 10 ? 'hm-red' : val < 14 ? 'hm-yellow' : 'hm-green';
td.textContent = val.toFixed(1);
sum += val; cnt++;
}
tr.appendChild(td);
});
// Cellule moyenne
const tdAvg = document.createElement('td');
if (cnt > 0) {
const avg = sum / cnt;
tdAvg.className = avg < 10 ? 'hm-red' : avg < 14 ? 'hm-yellow' : 'hm-green';
tdAvg.textContent = avg.toFixed(1);
tdAvg.style.fontWeight = '700';
} else {
tdAvg.className = 'hm-null';
tdAvg.textContent = '—';
}
tr.appendChild(tdAvg);
tbody.appendChild(tr);
});
})();
/* ══════════════════════════════════════════════════════
2. ÉVOLUTION MOYENNE GÉNÉRALE
null = séquence pas encore notée → pas de point tracé
══════════════════════════════════════════════════════ */
if (document.getElementById('c-evol')) {
new Chart('c-evol', {
type: 'line',
data: {
labels: seqLabels,
datasets: [{
label: 'Moyenne générale',
data : generalAvgBySeq,
borderColor: '#0f6e56', backgroundColor: 'rgba(15,110,86,.08)',
borderWidth: 2.5, pointBackgroundColor: '#0f6e56',
pointRadius: generalAvgBySeq.map(v => v !== null ? 5 : 0),
pointHoverRadius: 7, tension: .3, fill: true,
spanGaps: false, // ne pas relier les points si null entre eux
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
tooltip: { ...TIP, callbacks: { label: c => c.raw !== null ? ' Moy. : ' + c.raw + '/20' : ' Pas d\\'évaluation' } },
},
scales: {
x: { ...AX() },
y: { ...AX(), min: 0, max: 20, ticks: { ...AX().ticks, stepSize: 5 } },
},
},
plugins: [{
id: 'seuil',
afterDraw(ch) {
const { ctx, chartArea: { left, right }, scales: { y } } = ch;
const yp = y.getPixelForValue(10);
ctx.save();
ctx.beginPath(); ctx.moveTo(left, yp); ctx.lineTo(right, yp);
ctx.strokeStyle = 'rgba(163,45,45,.5)'; ctx.setLineDash([5, 4]);
ctx.lineWidth = 1.5; ctx.stroke();
ctx.fillStyle = 'rgba(163,45,45,.7)'; ctx.font = '11px DM Sans';
ctx.fillText('Seuil 10', left + 4, yp - 5);
ctx.restore();
}
}]
});
}
/* ══════════════════════════════════════════════════════
3. RÉUSSITE VS ÉCHEC (barres empilées)
Données depuis evaluation.success_h/f + faillures_h/f
→ pas depuis mark, déjà agrégées par le controller
══════════════════════════════════════════════════════ */
if (document.getElementById('c-success') && successFailData.labels.length > 0) {
new Chart('c-success', {
type: 'bar',
data: {
labels: successFailData.labels,
datasets: [
{
label: 'Réussite', data: successFailData.success,
backgroundColor: 'rgba(59,109,17,.55)', borderColor: C.em,
borderWidth: 1.5, borderRadius: 4, borderSkipped: false,
},
{
label: 'Échec', data: successFailData.failures,
backgroundColor: 'rgba(163,45,45,.55)', borderColor: C.rose,
borderWidth: 1.5, borderRadius: 4, borderSkipped: false,
},
{
label: 'Absents', data: successFailData.absents,
backgroundColor: 'rgba(133,79,11,.45)', borderColor: C.amber,
borderWidth: 1.5, borderRadius: 4, borderSkipped: false,
},
],
},
options: {
responsive: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { position: 'top', align: 'end', labels: { boxWidth: 9, boxHeight: 9, borderRadius: 3, useBorderRadius: true, color: C.txt, padding: 12 } },
tooltip: { ...TIP },
},
scales: {
x: { ...AX(), stacked: true, ticks: { ...AX().ticks, maxRotation: 35 } },
y: { ...AX(), stacked: true, beginAtZero: true },
},
},
});
}
/* ══════════════════════════════════════════════════════
4. RADAR PAR TRIMESTRE
Révèle le profil de la classe : quelle matière est
forte/faible, stable ou en progression entre trimestres
══════════════════════════════════════════════════════ */
if (document.getElementById('c-radar') && quaterProfiles.length > 0) {
new Chart('c-radar', {
type: 'radar',
data: {
labels: courseLabels,
datasets: quaterProfiles.map((qp, i) => ({
label : qp.label,
data : qp.data,
backgroundColor : PALQ[i % PALQ.length],
borderColor : PALQ_BD[i % PALQ_BD.length],
borderWidth : 2,
pointBackgroundColor: PALQ_BD[i % PALQ_BD.length],
pointRadius : 4,
spanGaps : true,
})),
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top', align: 'end',
labels: { boxWidth: 9, boxHeight: 9, borderRadius: 3, useBorderRadius: true, color: C.txt, padding: 12 }
},
tooltip: { ...TIP, callbacks: { label: c => ' ' + c.dataset.label + ' : ' + (c.raw !== null ? c.raw + '/20' : '—') } },
},
scales: {
r: {
min: 0, max: 20,
backgroundColor: 'rgba(24,95,165,.04)',
grid : { color: C.grid },
angleLines : { color: C.grid },
ticks : { color: C.dim, backdropColor: 'transparent', stepSize: 5 },
pointLabels : { color: C.dim, font: { size: 11 } },
},
},
},
});
}
/* ══════════════════════════════════════════════════════
5. DONUT EXAMENS OFFICIELS (seulement si classe examen)
══════════════════════════════════════════════════════ */
";
// line 583
if ((($tmp = CoreExtension::getAttribute($this->env, $this->source, (isset($context["classroom"]) || array_key_exists("classroom", $context) ? $context["classroom"] : (function () { throw new RuntimeError('Variable "classroom" does not exist.', 583, $this->source); })()), "apc", [], "any", false, false, false, 583)) && $tmp instanceof Markup ? (string) $tmp : $tmp)) {
// line 584
yield "if (document.getElementById('c-mentions') && mentionCountCategories.length > 0) {
new Chart('c-mentions', {
type: 'doughnut',
data: {
labels: mentionCategories,
datasets: [{
data: mentionCountCategories,
backgroundColor: PAL.slice(0, mentionCategories.length),
borderColor: '#f0f4f8', borderWidth: 3, hoverOffset: 8,
}],
},
options: {
responsive: true, cutout: '58%',
plugins: {
legend: { position: 'bottom', labels: { boxWidth: 8, boxHeight: 8, borderRadius: 2, useBorderRadius: true, color: C.txt, padding: 8, font: { size: 11 } } },
tooltip: { ...TIP, callbacks: { label: c => ' ' + c.label + ' : ' + c.raw } },
},
},
});
}
";
}
// line 605
yield "
/* ── Modales actions ──────────────────────────────────── */
\$(document).ready(function () {
\$('#form_modal_courses').on('show.bs.modal', function (e) {
\$('#form_modal_courses form').attr('action', \$(e.relatedTarget).data('action'));
});
\$('[data-toggle=\"modal\"]').on('click', function () {
\$('#form_modal_reportcard_params form').attr('action', \$(this).data('action'));
});
});
})();
</script>
";
$__internal_6f47bbe9983af81f1e7450e9a3e3768f->leave($__internal_6f47bbe9983af81f1e7450e9a3e3768f_prof);
$__internal_5a27a8ba21ca79b61932376b2fa922d2->leave($__internal_5a27a8ba21ca79b61932376b2fa922d2_prof);
yield from [];
}
/**
* @codeCoverageIgnore
*/
public function getTemplateName(): string
{
return "classroom/show.html.twig";
}
/**
* @codeCoverageIgnore
*/
public function isTraitable(): bool
{
return false;
}
/**
* @codeCoverageIgnore
*/
public function getDebugInfo(): array
{
return array ( 952 => 605, 929 => 584, 927 => 583, 707 => 365, 702 => 363, 697 => 362, 695 => 361, 691 => 360, 687 => 359, 683 => 358, 679 => 357, 675 => 356, 671 => 355, 667 => 354, 658 => 348, 645 => 347, 606 => 316, 593 => 313, 589 => 312, 586 => 311, 579 => 310, 568 => 301, 565 => 297, 560 => 293, 548 => 283, 546 => 282, 532 => 272, 521 => 260, 507 => 248, 498 => 240, 486 => 229, 483 => 224, 472 => 214, 466 => 213, 460 => 212, 458 => 211, 454 => 209, 451 => 208, 444 => 207, 441 => 206, 438 => 205, 430 => 204, 427 => 203, 425 => 202, 420 => 200, 416 => 199, 410 => 198, 406 => 197, 403 => 196, 398 => 195, 393 => 194, 391 => 193, 373 => 183, 367 => 182, 365 => 181, 362 => 180, 356 => 177, 352 => 176, 349 => 175, 347 => 174, 343 => 173, 339 => 172, 333 => 171, 330 => 170, 325 => 169, 323 => 168, 316 => 166, 309 => 161, 305 => 158, 300 => 156, 296 => 155, 292 => 154, 288 => 153, 284 => 152, 280 => 151, 275 => 150, 273 => 149, 269 => 148, 266 => 147, 261 => 143, 257 => 141, 255 => 140, 251 => 139, 246 => 136, 240 => 135, 236 => 134, 231 => 132, 227 => 130, 223 => 127, 210 => 126, 78 => 4, 65 => 3, 42 => 1,);
}
public function getSourceContext(): Source
{
return new Source("{% extends \"layout/backEndLayout.html.twig\" %}
{% block stylesheets %}
{{ parent() }}
<style>
/* ══════════════════════════════════════════════════════
TOKENS
══════════════════════════════════════════════════════ */
:root {
--bg : #f0f4f8; --s1 : #ffffff; --s2 : #e8edf3;
--bdr : rgba(0,0,0,.09); --bdr-hi: rgba(0,0,0,.18);
--blue : #185fa5; --rose : #a32d2d; --amber: #854f0b;
--teal : #0f6e56; --em : #3b6d11; --vi : #534ab7;
--txt : #1a2535; --dim : #4a5568; --muted: #7a8899;
--r: 14px; --font: 'DM Sans','Segoe UI',sans-serif;
}
/* ── Layout ──────────────────────────────────────────── */
.cr-page { font-family:var(--font); color:var(--txt); }
.cr-page * { box-sizing:border-box; }
/* ── Entête classe ───────────────────────────────────── */
.cr-head {
background:var(--s1); border:1px solid var(--bdr);
border-radius:var(--r); padding:20px 24px; margin-bottom:20px;
display:flex; align-items:center; justify-content:space-between;
flex-wrap:wrap; gap:14px;
}
.cr-head__name { font-size:22px; font-weight:700; letter-spacing:-.02em; }
.cr-head__meta { font-size:13px; color:var(--dim); }
.cr-head__badges { display:flex; gap:8px; flex-wrap:wrap; }
.badge-pill {
display:inline-flex; align-items:center; gap:5px; padding:5px 12px;
border-radius:20px; font-size:12px; font-weight:500; border:1px solid var(--bdr);
}
.bp-blue { background:rgba(59,130,246,.15); color:var(--blue); }
.bp-amber { background:rgba(245,158,11,.15); color:var(--amber); }
.bp-teal { background:rgba(20,184,166,.15); color:var(--teal); }
/* ── Actions ─────────────────────────────────────────── */
.cr-actions {
background:var(--s1); border:1px solid var(--bdr); border-radius:var(--r);
padding:14px 18px; margin-bottom:20px; display:flex; flex-wrap:wrap; gap:8px;
}
.btn-cr {
display:inline-flex; align-items:center; gap:6px; padding:7px 14px;
border-radius:8px; font-size:12.5px; font-weight:500; text-decoration:none;
border:1px solid var(--bdr); cursor:pointer; transition:background .18s,transform .12s;
color:var(--txt); background:var(--s2);
}
.btn-cr:hover { transform:translateY(-1px); border-color:var(--bdr-hi); color:var(--txt); }
.btn-cr-blue { background:rgba(59,130,246,.15); color:var(--blue); border-color:rgba(59,130,246,.3); }
.btn-cr-amber { background:rgba(245,158,11,.15); color:var(--amber); border-color:rgba(245,158,11,.3); }
.btn-cr-teal { background:rgba(20,184,166,.15); color:var(--teal); border-color:rgba(20,184,166,.3); }
.btn-cr-rose { background:rgba(244,63,94,.15); color:var(--rose); border-color:rgba(244,63,94,.3); }
.btn-cr-vi { background:rgba(139,92,246,.15); color:var(--vi); border-color:rgba(139,92,246,.3); }
.btn-cr-em { background:rgba(16,185,129,.15); color:var(--em); border-color:rgba(16,185,129,.3); }
/* ── Tables ──────────────────────────────────────────── */
.cr-table-wrap {
background:var(--s1); border:1px solid var(--bdr); border-radius:var(--r);
overflow:hidden; margin-bottom:20px;
}
.cr-table-title {
padding:14px 18px 12px; border-bottom:1px solid var(--bdr);
font-size:14px; font-weight:600; display:flex; align-items:center; gap:8px;
}
.cr-table-title .dot { width:7px;height:7px;border-radius:50%; }
.d-bl{background:var(--blue);box-shadow:0 0 5px var(--blue);}
.d-am{background:var(--amber);box-shadow:0 0 5px var(--amber);}
table.cr-tbl { width:100%; border-collapse:collapse; font-size:13px; }
table.cr-tbl th { padding:10px 14px; color:var(--dim); font-weight:500; font-size:11.5px; text-transform:uppercase; letter-spacing:.05em; border-bottom:1px solid var(--bdr); background:var(--s2); }
table.cr-tbl td { padding:9px 14px; border-bottom:1px solid rgba(0,0,0,.05); }
table.cr-tbl tr:last-child td { border-bottom:none; }
table.cr-tbl tr:hover td { background:rgba(0,0,0,.025); }
.tag-exam { display:inline-block; background:rgba(244,63,94,.15); color:var(--rose); padding:2px 8px; border-radius:4px; font-size:11px; }
/* ── Grille graphiques ───────────────────────────────── */
.chart-grid { display:grid; gap:16px; margin-bottom:16px; }
.cg-2 { grid-template-columns:1fr 1fr; }
.cg-1 { grid-template-columns:1fr; }
@media(max-width:900px){ .cg-2 { grid-template-columns:1fr; } }
/* ── Carte graphique ─────────────────────────────────── */
.cc {
background:var(--s1); border:1px solid var(--bdr); border-radius:var(--r);
overflow:hidden; transition:border-color .2s; animation:cIn .4s ease both;
}
.cc:hover { border-color:var(--bdr-hi); }
.cc__hd {
display:flex; align-items:center; justify-content:space-between;
padding:13px 18px 11px; border-bottom:1px solid var(--bdr);
}
.cc__lbl { display:flex; align-items:center; gap:8px; font-size:13px; font-weight:600; }
.cc__dot { width:7px;height:7px;border-radius:50%;flex-shrink:0; }
.d-tl{background:var(--teal);box-shadow:0 0 5px var(--teal);}
.d-rs{background:var(--rose);box-shadow:0 0 5px var(--rose);}
.d-vi{background:var(--vi);box-shadow:0 0 5px var(--vi);}
.d-em{background:var(--em);box-shadow:0 0 5px var(--em);}
.cc__sub { font-size:11px; color:var(--muted); }
.cc__bd { padding:16px 18px 18px; }
/* ── Heatmap ─────────────────────────────────────────── */
.heatmap { overflow-x:auto; }
.heatmap table { width:100%; border-collapse:collapse; font-size:12px; }
.heatmap th { padding:7px 10px; color:var(--dim); font-size:11px; text-transform:uppercase; letter-spacing:.05em; text-align:center; font-weight:500; }
.heatmap th.course-th { text-align:left; min-width:140px; }
.heatmap td { padding:5px 8px; text-align:center; border:1px solid rgba(0,0,0,.05); font-weight:500; font-size:12.5px; border-radius:4px; transition:opacity .2s; }
.heatmap td:hover { opacity:.8; }
.heatmap td.course-td { text-align:left; color:var(--txt); font-weight:400; font-size:12px; padding-left:4px; background:transparent !important; border:none; }
.hm-null { background:rgba(0,0,0,.05); color:var(--muted); }
.hm-red { background:rgba(163,45,45,.15); color:#7f1d1d; }
.hm-yellow { background:rgba(133,79,11,.15); color:#451a03; }
.hm-green { background:rgba(15,110,86,.15); color:#064e3b; }
@keyframes cIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.cc:nth-child(1){animation-delay:.04s}.cc:nth-child(2){animation-delay:.1s}
#c-evol { max-height:220px; width:100%!important; }
#c-success{ max-height:260px; width:100%!important; }
#c-radar { max-height:300px; width:100%!important; }
#c-mentions{ max-height:220px; max-width:220px; margin:auto; display:block; }
</style>
{% endblock %}
{% block content %}
<div class=\"cr-page p-3\">
{# ── Entête classe ────────────────────────────────────────────── #}
<div class=\"cr-head\">
<div>
<div class=\"cr-head__name\">{{ classroom.name }}</div>
<div class=\"cr-head__meta\">
Niveau {{ classroom.level }}
{% if mainteacher %} · Titulaire : <strong>{{ mainteacher.fullName }}</strong>{% endif %}
</div>
</div>
<div class=\"cr-head__badges\">
<span class=\"badge-pill bp-blue\"><i class=\"fa fa-users\"></i> {{ studentEnrolled|length }} élèves</span>
{% if classroom.apc %}
<span class=\"badge-pill bp-amber\"><i class=\"fa fa-graduation-cap\"></i> Classe d'examen</span>
{% endif %}
</div>
</div>
{# ── Boutons actions ──────────────────────────────────────────── #}
<div class=\"cr-actions\">
<a class=\"btn-cr btn-cr-blue\" href=\"{{ path('admin_classrooms') }}\"><i class=\"fa fa-list\"></i> Liste</a>
{% if is_granted('ROLE_ADMIN') %}
<a class=\"btn-cr btn-cr-blue\" href=\"{{ path('admin_classrooms_edit', {id: classroom.id}) }}\"><i class=\"fa fa-edit\"></i> Modifier</a>
<a class=\"btn-cr btn-cr-amber\" data-toggle=\"modal\" data-target=\"#form_modal_courses\" data-action=\"{{ path('admin_classrooms_recapitulatif_seq', {id: classroom.id}) }}\"><i class=\"fa fa-file-pdf-o\"></i> Recap Seq</a>
<a class=\"btn-cr btn-cr-amber\" href=\"{{ path('admin_classrooms_reportcards_seq', {id: classroom.id}) }}\"><i class=\"fa fa-file\"></i> Bull Seq</a>
<a class=\"btn-cr btn-cr-teal\" data-toggle=\"modal\" data-target=\"#form_modal_reportcard_params\" data-action=\"{{ path('admin_classrooms_reportcards_trim_2024', {id: classroom.id}) }}\"><i class=\"fa fa-file\"></i> Bull Trim</a>
<a class=\"btn-cr btn-cr-vi\" data-toggle=\"modal\" data-target=\"#form_modal_reportcard_params\" data-action=\"{{ path('admin_class_reportcards_year_2024', {id: classroom.id}) }}\"><i class=\"fa fa-file-pdf-o\"></i> Bull Ann</a>
<a class=\"btn-cr btn-cr-rose\" data-toggle=\"modal\" data-target=\"#form_modal_courses\" data-action=\"{{ path('admin_classrooms_recapitulatif_trim', {id: classroom.id}) }}\"><i class=\"fa fa-file-pdf-o\"></i> Recap Trim</a>
<a class=\"btn-cr btn-cr-em\" data-toggle=\"modal\" data-target=\"#form_modal_courses\" data-action=\"{{ path('admin_classrooms_recapitulatif_ann_excel', {id: classroom.id}) }}\"><i class=\"fa fa-file-excel-o\"></i> Recap Ann</a>
{% endif %}
</div>
{# ── Tables élèves + matières ─────────────────────────────────── #}
<div class=\"cg-2 chart-grid\">
<div class=\"cr-table-wrap\">
<div class=\"cr-table-title\"><span class=\"dot d-bl\"></span>Élèves inscrits</div>
<table class=\"cr-tbl\">
<thead><tr><th>Matricule</th><th>Nom</th><th>Prénom</th>{% if is_granted('ROLE_ADMIN') %}<th>Action</th>{% endif %}</tr></thead>
<tbody>
{% set effectif = 0 %}
{% for std in studentEnrolled %}
<tr>
<td><a href=\"{{ path('admin_students_show', {id: std.id}) }}\" style=\"color:var(--blue)\">{{ std.matricule }}</a></td>
<td>{{ std.lastname }}</td>
<td>{{ std.firstname }}</td>
{% if is_granted('ROLE_ADMIN') %}
<td>
<a class=\"btn-cr btn-cr-rose\" style=\"padding:3px 8px;font-size:11px\" href=\"{{ path('admin_students_unregister', {id: std.id, room_id: classroom.id}) }}\"><i class=\"fa fa-ban\"></i></a>
<a class=\"btn-cr\" style=\"padding:3px 8px;font-size:11px\" href=\"{{ path('admin_students_edit', {id: std.id}) }}\"><i class=\"fa fa-pencil-square-o\"></i></a>
</td>
{% endif %}
</tr>
{% set effectif = effectif + 1 %}
{% endfor %}
<tr><td colspan=\"2\" style=\"color:var(--dim);font-size:11.5px\">Effectif total</td><td style=\"font-weight:700;color:var(--blue)\">{{ effectif }}</td>{% if is_granted('ROLE_ADMIN') %}<td></td>{% endif %}</tr>
</tbody>
</table>
</div>
<div class=\"cr-table-wrap\">
<div class=\"cr-table-title\"><span class=\"dot d-am\"></span>Matières programmées</div>
<table class=\"cr-tbl\">
<thead><tr><th>Module</th><th>Code</th><th>Intitulé</th><th>Coef</th><th>Enseignant</th></tr></thead>
<tbody>
{% set totalCoef = 0 %}
{% for module in modules %}
{% for course in module.courses %}
<tr>
<td style=\"color:var(--dim);font-size:11.5px\">{{ module.name }}</td>
<td><a href=\"{{ path('admin_courses_show', {id: course.id}) }}\" style=\"color:var(--blue)\">{{ course.code }}</a></td>
<td>{{ course.wording }}</td>
<td style=\"font-weight:600\">{{ course.coefficient }}</td>
<td>
{% if attributions[course.id] is defined %}
{% if is_granted('ROLE_ADMIN') %}
<a href=\"{{ path('admin_attributions_edit', {id: attributions[course.id].id}) }}\" style=\"color:var(--teal)\">{{ attributions[course.id].teacher.fullName }}</a>
{% else %}{{ attributions[course.id].teacher.fullName }}{% endif %}
{% else %}
{% if is_granted('ROLE_ADMIN') %}<a href=\"{{ path('admin_attributions_new') }}\" style=\"color:var(--rose)\">Non attribué</a>{% endif %}
{% endif %}
</td>
</tr>
{% set totalCoef = totalCoef + course.coefficient %}
{% endfor %}
{% endfor %}
<tr><td colspan=\"3\" style=\"color:var(--dim);font-size:11.5px\">Total coefficients</td><td style=\"font-weight:700;color:var(--amber)\" colspan=\"2\">{{ totalCoef }}</td></tr>
</tbody>
</table>
</div>
</div>
{# ══════════════════════════════════════════════════════════════
SECTION GRAPHIQUES
══════════════════════════════════════════════════════════════ #}
{# ── Graphique 1 : Heatmap cours × séquences ──────────────────
Résout le problème original : chaque cellule est indépendante,
null (pas d'éval) = gris neutre, aucun décalage possible.
─────────────────────────────────────────────────────────────── #}
<div class=\"cc\" style=\"margin-bottom:16px\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-am\"></span>Performances séquentielles — toutes matières</div>
<span class=\"cc__sub\">rouge <10 · jaune 10–13 · vert ≥14 · gris = pas d'évaluation enregistrée</span>
</div>
<div class=\"cc__bd heatmap\">
<table id=\"heatmap-table\">
<thead>
<tr>
<th class=\"course-th\">Matière</th>
{# Les en-têtes de séquences sont injectées par JS #}
</tr>
</thead>
<tbody id=\"heatmap-body\"></tbody>
</table>
</div>
</div>
{# ── Graphique 2 + 3 : Évolution + Réussite/Échec ─────────── #}
<div class=\"chart-grid cg-2\">
<div class=\"cc\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-tl\"></span>Évolution de la moyenne générale</div>
<span class=\"cc__sub\">séquence par séquence · ligne rouge = seuil 10</span>
</div>
<div class=\"cc__bd\"><canvas id=\"c-evol\"></canvas></div>
</div>
<div class=\"cc\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-rs\"></span>Réussite vs Échec — {{ activeSeqLabel }}</div>
<span class=\"cc__sub\">séquence active · avec absents</span>
</div>
<div class=\"cc__bd\"><canvas id=\"c-success\"></canvas></div>
</div>
</div>
{# ── Graphique 4 + 5 : Radar + Donut ─────────────────────────
Radar : profil trimestriel de la classe (points forts/faibles)
Donut : examens officiels (seulement si classe d'examen)
─────────────────────────────────────────────────────────────── #}
<div class=\"chart-grid {{ classroom.apc ? 'cg-2' : 'cg-1' }}\">
<div class=\"cc\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-vi\"></span>Profil trimestriel de la classe</div>
<span class=\"cc__sub\">radar par matière · T1, T2, T3</span>
</div>
<div class=\"cc__bd\"><canvas id=\"c-radar\"></canvas></div>
</div>
{% if classroom.apc %}
<div class=\"cc\">
<div class=\"cc__hd\">
<div class=\"cc__lbl\"><span class=\"cc__dot d-em\"></span>Résultats examens officiels</div>
<span class=\"cc__sub\">répartition des mentions</span>
</div>
<div class=\"cc__bd\" style=\"display:flex;align-items:center;justify-content:center\">
<canvas id=\"c-mentions\"></canvas>
</div>
</div>
{% endif %}
</div>
</div>{# /.cr-page #}
{# ══════════════════════════════════════════════════════════════
MODALES (inchangées)
══════════════════════════════════════════════════════════════ #}
<div class=\"modal fade\" id=\"form_modal_courses\" tabindex=\"-1\" role=\"dialog\">
<div class=\"modal-dialog modal-dialog-centered\">
<div class=\"modal-content\" style=\"background:#ffffff;color:#1a2535;border:1px solid rgba(0,0,0,.12)\">
<div class=\"modal-header border-bottom-0\">
<h5 class=\"modal-title\">Choisir les cours du récapitulatif</h5>
<button type=\"button\" class=\"close\" data-dismiss=\"modal\" style=\"color:var(--txt)\"><span>×</span></button>
</div>
<form action=\"#\" method=\"post\">
<div class=\"modal-body\">
{% for module in modules %}{% for course in module.courses %}
<li class=\"list-group-item\" style=\"background:transparent;border-color:var(--bdr);color:var(--txt)\">
<input class=\"form-check-input me-1\" type=\"checkbox\" checked name=\"selected_courses[]\" value=\"{{ course.id }}\">
{{ course.wording }}
</li>
{% endfor %}{% endfor %}
</div>
<div class=\"modal-footer border-top-0 d-flex justify-content-center\">
<button type=\"submit\" class=\"btn-cr btn-cr-em\">Générer</button>
</div>
</form>
</div>
</div>
</div>
<div class=\"modal fade\" id=\"form_modal_reportcard_params\" tabindex=\"-1\" role=\"dialog\">
<div class=\"modal-dialog modal-dialog-centered\">
<div class=\"modal-content\" style=\"background:#ffffff;color:#1a2535;border:1px solid rgba(0,0,0,.12)\">
<div class=\"modal-header border-bottom-0\">
<h5 class=\"modal-title\">Paramètres du bulletin</h5>
<button type=\"button\" class=\"close\" data-dismiss=\"modal\" style=\"color:var(--txt)\"><span>×</span></button>
</div>
<form method=\"post\">
<div class=\"modal-body\">
<div class=\"form-check mb-2\"><input class=\"form-check-input\" type=\"checkbox\" name=\"copyright\" id=\"copyright\"><label class=\"form-check-label\" for=\"copyright\">Copyright en pied de page</label></div>
<div class=\"form-check\"><input class=\"form-check-input\" type=\"checkbox\" name=\"reverse\" id=\"reverse\"><label class=\"form-check-label\" for=\"reverse\">Sens inverse</label></div>
</div>
<div class=\"modal-footer border-top-0 d-flex justify-content-center\">
<button type=\"submit\" class=\"btn-cr btn-cr-blue\">Générer</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script>
(function(){
'use strict';
/* ── Données depuis le controller ─────────────────────── */
const courseLabels = {{ courseLabels | raw }};
const seqLabels = {{ seqLabels | raw }};
const seqIds = {{ seqIds | raw }};
const heatmapData = {{ heatmapData | raw }};
const generalAvgBySeq = {{ generalAvgBySeq | raw }};
const successFailData = {{ successFailData | raw }};
const quaterProfiles = {{ quaterProfiles | raw }};
{% if classroom.apc %}
const mentionCategories = {{ mentionCategories | raw }};
const mentionCountCategories = {{ mentionCountCategories | raw }};
{% endif %}
/* ── Palette & defaults ───────────────────────────────── */
const C = {
blue:'#185fa5', rose:'#a32d2d', amber:'#854f0b',
teal:'#0f6e56', vi:'#534ab7', em:'#3b6d11', orange:'#9a4f0b',
grid:'rgba(0,0,0,.07)', bg:'#ffffff', txt:'#1a2535', dim:'#4a5568'
};
const PALQ = ['rgba(24,95,165,.35)','rgba(15,110,86,.35)','rgba(83,74,183,.35)'];
const PALQ_BD = ['#185fa5', '#0f6e56', '#534ab7'];
const PAL = ['#3b82f6','#f59e0b','#f43f5e','#10b981','#8b5cf6','#14b8a6','#f97316','#84cc16'];
Chart.defaults.color = C.dim;
Chart.defaults.font.family = \"'DM Sans','Segoe UI',sans-serif\";
Chart.defaults.font.size = 12;
const TIP = {backgroundColor:'#ffffff',borderColor:'rgba(0,0,0,.12)',borderWidth:1,titleColor:C.txt,bodyColor:C.dim,padding:12,cornerRadius:10};
const AX = (o={}) => ({grid:{color:C.grid,drawBorder:false},ticks:{color:C.dim},border:{display:false},...o});
/* ══════════════════════════════════════════════════════
1. HEATMAP — table HTML colorée cours × séquences
Résout le problème de départ : chaque cellule est
indépendante. null = gris \"pas d'éval enregistrée\".
══════════════════════════════════════════════════════ */
(function buildHeatmap(){
const thead = document.querySelector('#heatmap-table thead tr');
const tbody = document.getElementById('heatmap-body');
// En-têtes séquences
seqLabels.forEach(lbl => {
const th = document.createElement('th');
th.textContent = lbl;
thead.appendChild(th);
});
// Colonne moyenne
const thAvg = document.createElement('th');
thAvg.textContent = 'Moy.';
thead.appendChild(thAvg);
// Lignes de données
heatmapData.forEach(row => {
const tr = document.createElement('tr');
// Cellule nom de la matière
const tdName = document.createElement('td');
tdName.className = 'course-td';
tdName.textContent = row.course;
tr.appendChild(tdName);
let sum = 0, cnt = 0;
// Cellules par séquence
seqIds.forEach(sid => {
const val = row['s' + sid];
const td = document.createElement('td');
if (val === null || val === undefined) {
td.className = 'hm-null';
td.textContent = '—';
} else {
td.className = val < 10 ? 'hm-red' : val < 14 ? 'hm-yellow' : 'hm-green';
td.textContent = val.toFixed(1);
sum += val; cnt++;
}
tr.appendChild(td);
});
// Cellule moyenne
const tdAvg = document.createElement('td');
if (cnt > 0) {
const avg = sum / cnt;
tdAvg.className = avg < 10 ? 'hm-red' : avg < 14 ? 'hm-yellow' : 'hm-green';
tdAvg.textContent = avg.toFixed(1);
tdAvg.style.fontWeight = '700';
} else {
tdAvg.className = 'hm-null';
tdAvg.textContent = '—';
}
tr.appendChild(tdAvg);
tbody.appendChild(tr);
});
})();
/* ══════════════════════════════════════════════════════
2. ÉVOLUTION MOYENNE GÉNÉRALE
null = séquence pas encore notée → pas de point tracé
══════════════════════════════════════════════════════ */
if (document.getElementById('c-evol')) {
new Chart('c-evol', {
type: 'line',
data: {
labels: seqLabels,
datasets: [{
label: 'Moyenne générale',
data : generalAvgBySeq,
borderColor: '#0f6e56', backgroundColor: 'rgba(15,110,86,.08)',
borderWidth: 2.5, pointBackgroundColor: '#0f6e56',
pointRadius: generalAvgBySeq.map(v => v !== null ? 5 : 0),
pointHoverRadius: 7, tension: .3, fill: true,
spanGaps: false, // ne pas relier les points si null entre eux
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
tooltip: { ...TIP, callbacks: { label: c => c.raw !== null ? ' Moy. : ' + c.raw + '/20' : ' Pas d\\'évaluation' } },
},
scales: {
x: { ...AX() },
y: { ...AX(), min: 0, max: 20, ticks: { ...AX().ticks, stepSize: 5 } },
},
},
plugins: [{
id: 'seuil',
afterDraw(ch) {
const { ctx, chartArea: { left, right }, scales: { y } } = ch;
const yp = y.getPixelForValue(10);
ctx.save();
ctx.beginPath(); ctx.moveTo(left, yp); ctx.lineTo(right, yp);
ctx.strokeStyle = 'rgba(163,45,45,.5)'; ctx.setLineDash([5, 4]);
ctx.lineWidth = 1.5; ctx.stroke();
ctx.fillStyle = 'rgba(163,45,45,.7)'; ctx.font = '11px DM Sans';
ctx.fillText('Seuil 10', left + 4, yp - 5);
ctx.restore();
}
}]
});
}
/* ══════════════════════════════════════════════════════
3. RÉUSSITE VS ÉCHEC (barres empilées)
Données depuis evaluation.success_h/f + faillures_h/f
→ pas depuis mark, déjà agrégées par le controller
══════════════════════════════════════════════════════ */
if (document.getElementById('c-success') && successFailData.labels.length > 0) {
new Chart('c-success', {
type: 'bar',
data: {
labels: successFailData.labels,
datasets: [
{
label: 'Réussite', data: successFailData.success,
backgroundColor: 'rgba(59,109,17,.55)', borderColor: C.em,
borderWidth: 1.5, borderRadius: 4, borderSkipped: false,
},
{
label: 'Échec', data: successFailData.failures,
backgroundColor: 'rgba(163,45,45,.55)', borderColor: C.rose,
borderWidth: 1.5, borderRadius: 4, borderSkipped: false,
},
{
label: 'Absents', data: successFailData.absents,
backgroundColor: 'rgba(133,79,11,.45)', borderColor: C.amber,
borderWidth: 1.5, borderRadius: 4, borderSkipped: false,
},
],
},
options: {
responsive: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { position: 'top', align: 'end', labels: { boxWidth: 9, boxHeight: 9, borderRadius: 3, useBorderRadius: true, color: C.txt, padding: 12 } },
tooltip: { ...TIP },
},
scales: {
x: { ...AX(), stacked: true, ticks: { ...AX().ticks, maxRotation: 35 } },
y: { ...AX(), stacked: true, beginAtZero: true },
},
},
});
}
/* ══════════════════════════════════════════════════════
4. RADAR PAR TRIMESTRE
Révèle le profil de la classe : quelle matière est
forte/faible, stable ou en progression entre trimestres
══════════════════════════════════════════════════════ */
if (document.getElementById('c-radar') && quaterProfiles.length > 0) {
new Chart('c-radar', {
type: 'radar',
data: {
labels: courseLabels,
datasets: quaterProfiles.map((qp, i) => ({
label : qp.label,
data : qp.data,
backgroundColor : PALQ[i % PALQ.length],
borderColor : PALQ_BD[i % PALQ_BD.length],
borderWidth : 2,
pointBackgroundColor: PALQ_BD[i % PALQ_BD.length],
pointRadius : 4,
spanGaps : true,
})),
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top', align: 'end',
labels: { boxWidth: 9, boxHeight: 9, borderRadius: 3, useBorderRadius: true, color: C.txt, padding: 12 }
},
tooltip: { ...TIP, callbacks: { label: c => ' ' + c.dataset.label + ' : ' + (c.raw !== null ? c.raw + '/20' : '—') } },
},
scales: {
r: {
min: 0, max: 20,
backgroundColor: 'rgba(24,95,165,.04)',
grid : { color: C.grid },
angleLines : { color: C.grid },
ticks : { color: C.dim, backdropColor: 'transparent', stepSize: 5 },
pointLabels : { color: C.dim, font: { size: 11 } },
},
},
},
});
}
/* ══════════════════════════════════════════════════════
5. DONUT EXAMENS OFFICIELS (seulement si classe examen)
══════════════════════════════════════════════════════ */
{% if classroom.apc %}
if (document.getElementById('c-mentions') && mentionCountCategories.length > 0) {
new Chart('c-mentions', {
type: 'doughnut',
data: {
labels: mentionCategories,
datasets: [{
data: mentionCountCategories,
backgroundColor: PAL.slice(0, mentionCategories.length),
borderColor: '#f0f4f8', borderWidth: 3, hoverOffset: 8,
}],
},
options: {
responsive: true, cutout: '58%',
plugins: {
legend: { position: 'bottom', labels: { boxWidth: 8, boxHeight: 8, borderRadius: 2, useBorderRadius: true, color: C.txt, padding: 8, font: { size: 11 } } },
tooltip: { ...TIP, callbacks: { label: c => ' ' + c.label + ' : ' + c.raw } },
},
},
});
}
{% endif %}
/* ── Modales actions ──────────────────────────────────── */
\$(document).ready(function () {
\$('#form_modal_courses').on('show.bs.modal', function (e) {
\$('#form_modal_courses form').attr('action', \$(e.relatedTarget).data('action'));
});
\$('[data-toggle=\"modal\"]').on('click', function () {
\$('#form_modal_reportcard_params form').attr('action', \$(this).data('action'));
});
});
})();
</script>
{% endblock %}
", "classroom/show.html.twig", "/var/www/bethesda/templates/classroom/show.html.twig");
}
}