{% macro donut(pct, size, stroke, bg, fg, label) %} {# Clamp entre 0 et 100 manuellement #} {% set raw = pct ?? 0 %} {% set clamped = raw < 0 ? 0 : (raw > 100 ? 100 : raw) %} {% set r = (size - stroke) / 2 %} {% set dash = (clamped / 100) * (2 * 3.14159 * r) %} {% endmacro %} {# === Double donut (E/A et D/E) avec légende standardisée === #} {% macro double_donut(allocated, committed, disbursed, size, showLegend=true, showPctInLegend=true) %} {% import _self as M %} {# -- Palette KPLAN (cohérente avec les autres graphiques) -- #} {% set COL_OUTER = '#ff5b5b' %} {# Engagé (E) #} {% set COL_INNER = '#10C469' %} {# Décaissé (D) #} {% set BG_OUTER = '#ffbc00' %} {% set BG_INNER = '#eaeaea' %} {# -- Dimensions -- #} {% set size = size|default(80) %} {% set stroke = (size * 0.22)|round(0, 'floor') %} {# -- Safe divide + clamp -- #} {% set E_over_A = (allocated and allocated > 0) ? (100.0 * (committed|default(0)) / allocated) : 0 %} {% set D_over_E = (committed and committed > 0) ? (100.0 * (disbursed|default(0)) / committed) : 0 %} {% set outerPct = E_over_A < 0 ? 0 : (E_over_A > 100 ? 100 : E_over_A) %} {% set innerPct = D_over_E < 0 ? 0 : (D_over_E > 100 ? 100 : D_over_E) %} {# -- Mise en page : donut + légende à droite -- #} {% set legendW = showLegend ? 120 : 0 %} {% set totalW = size + (showLegend ? legendW + 8 : 0) %} {% set totalH = size %} {% endmacro %} {% macro bar_pair(allocated, committed, disbursed) %} {% set A = (allocated ?? 0) %} {% set C = (committed ?? 0) %} {% set D = (disbursed ?? 0) %} {% set w = 300 %}{% set h = 14 %} {% set cW = A > 0 ? (C/A * w) : 0 %} {% set dW = A > 0 ? (D/A * w) : 0 %} {% set cLabelX = cW + 6 > w - 6 ? w - 6 : cW + 6 %} {% set dLabelX = dW + 6 > w - 6 ? w - 6 : dW + 6 %} {% endmacro %} {% macro spark(values, w, h, variant='auto', strokeWidth=1.6, showDot=true) %} {% set w = w|default(120) %} {% set h = h|default(36) %} {% set v = values|default([]) %} {# Palette via CSS variables #} {% set colorPrimary = 'var(--primary-color)' %} {# vert #} {% set colorSecondary = 'var(--secondary-color)' %} {# jaune #} {% set colorAccent = 'var(--accent-color)' %} {# rouge #} {% set colorSuccess = 'var(--success-color)' %} {% set colorWarning = 'var(--warning-color)' %} {% set colorDanger = 'var(--danger-color)' %} {% set colorMuted = 'var(--border-dark)' %} {# Choix couleur #} {% set strokeColor = variant == 'primary' ? colorPrimary : variant == 'secondary' ? colorSecondary : variant == 'accent' ? colorAccent : variant == 'success' ? colorSuccess : variant == 'warning' ? colorWarning : variant == 'danger' ? colorDanger : null %} {% if v is empty %} {% else %} {% set sorted = v|sort %} {% set minv = sorted|first %} {% set maxv = sorted|last %} {% set span = (maxv - minv) == 0 ? 1 : (maxv - minv) %} {% set n = v|length %} {# Couleur auto selon tendance si variant='auto' #} {% if strokeColor is null %} {% set trend = (v|last) - (v|first) %} {% set strokeColor = trend > 0 ? colorSuccess : (trend < 0 ? colorDanger : colorSecondary) %} {% endif %} {% if n < 2 %} {% set y = 3 + (1 - ((v|first - minv)/span)) * (h - 6) %} {% else %} {% set step = (w - 6) / (n - 1) %} {% set pts = [] %} {% for i, val in v %} {% set x = 3 + i * step %} {% set y = 3 + (1 - ((val - minv)/span)) * (h - 6) %} {% set pts = pts|merge([x ~ ',' ~ y]) %} {% endfor %} {% set lastX = 3 + (n - 1) * step %} {% set lastY = 3 + (1 - ((v|last - minv)/span)) * (h - 6) %} {% endif %} {% endif %} {% endmacro %} {% macro spark_area(values, w, h, variant='auto', strokeWidth=1.6, showDot=true) %} {% set w = w|default(140) %} {% set h = h|default(44) %} {% set v = values|default([]) %} {% set colorPrimary = 'var(--primary-color)' %} {# vert #} {% set colorSecondary = 'var(--secondary-color)' %} {# jaune #} {% set colorAccent = 'var(--accent-color)' %} {# rouge #} {% set colorSuccess = 'var(--success-color)' %} {% set colorWarning = 'var(--warning-color)' %} {% set colorDanger = 'var(--danger-color)' %} {% set colorMuted = 'var(--border-dark)' %} {% set strokeColor = variant == 'primary' ? colorPrimary : variant == 'secondary' ? colorSecondary : variant == 'accent' ? colorAccent : variant == 'success' ? colorSuccess : variant == 'warning' ? colorWarning : variant == 'danger' ? colorDanger : null %} {% if v is empty %} {% else %} {% set sorted = v|sort %} {% set minv = sorted|first %} {% set maxv = sorted|last %} {% set span = (maxv - minv) == 0 ? 1 : (maxv - minv) %} {% set n = v|length %} {# Couleur auto si non forcée #} {% if strokeColor is null %} {% set trend = (v|last) - (v|first) %} {% set strokeColor = trend > 0 ? colorSuccess : (trend < 0 ? colorDanger : colorSecondary) %} {% endif %} {% set step = n < 2 ? (w - 6) : (w - 6) / (n - 1) %} {% set pts = [] %} {% for i, val in v %} {% set x = 3 + i * step %} {% set y = 3 + (1 - ((val - minv)/span)) * (h - 6) %} {% set pts = pts|merge([x ~ ',' ~ y]) %} {% endfor %} {% set lastX = 3 + (n - 1) * step %} {% set lastY = 3 + (1 - ((v|last - minv)/span)) * (h - 6) %} {% set baseY = h - 3 %} {% set areaPts = (pts|join(' ')) ~ ' ' ~ lastX ~ ',' ~ baseY ~ ' ' ~ 3 ~ ',' ~ baseY %} {# id de dégradé simple pour éviter les collisions courantes #} {% set gid = 'grad-' ~ w ~ '-' ~ h ~ '-' ~ n %} {% endif %} {% endmacro %} {% macro gantt_svg(activities, width = 794, height = 200, BAR_HEIGHT = 10, ROW_GAP = 6) %} {% if activities is empty %}