doc database

This commit is contained in:
2026-05-14 09:19:03 +02:00
parent e1e1174db7
commit ed67438739
6 changed files with 1051 additions and 10 deletions
@@ -0,0 +1,519 @@
/* ============================================================
doc_database.css
Feuille de style de la documentation base de données
Dépendances : w3.css, all.css (Font Awesome)
Fond sombre, style industriel/technique
============================================================ */
/* ─── Variables globales ─── */
:root {
--bg-base: #0d1117;
--bg-surface: #161b22;
--bg-card: #1c2128;
--bg-card-hover: #21262d;
--border: #30363d;
--border-strong: #484f58;
--text-primary: #e6edf3;
--text-secondary: #8b949e;
--text-muted: #6e7681;
--text-code: #79c0ff;
--accent-blue: #58a6ff;
--accent-cyan: #39d353;
--accent-green: #3fb950;
--accent-teal: #2dd4bf;
--accent-orange: #f0883e;
--accent-yellow: #e3b341;
--accent-red: #f85149;
--accent-purple: #bc8cff;
--accent-indigo: #818cf8;
--nav-width: 240px;
--header-h: 90px;
--radius: 8px;
--radius-sm: 4px;
--shadow: 0 4px 16px rgba(0,0,0,.4);
--font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
--font-body: 'Segoe UI', system-ui, sans-serif;
}
/* ─── Reset minimal ─── */
*, *::before, *::after { box-sizing: border-box; }
a { color: var(--accent-blue); text-decoration: none; }
a:hover { text-decoration: underline; }
ul { padding: 0; margin: 0; list-style: none; }
/* ─── Mise en page principale ─── */
.doc-wrapper {
display: grid;
grid-template-columns: var(--nav-width) 1fr;
grid-template-rows: auto 1fr;
grid-template-areas:
"header header"
"sidenav content";
min-height: 100vh;
background: var(--bg-base);
color: var(--text-primary);
font-family: var(--font-body);
font-size: 14px;
line-height: 1.6;
}
/* ─── En-tête ─── */
.doc-header {
grid-area: header;
background: linear-gradient(135deg, #1a2236 0%, #0d1117 60%, #1a1a2e 100%);
border-bottom: 1px solid var(--border-strong);
padding: 20px 28px;
}
.doc-header-inner {
display: flex;
align-items: center;
gap: 18px;
max-width: 1200px;
}
.doc-header-icon {
width: 52px; height: 52px;
background: linear-gradient(135deg, var(--accent-blue), var(--accent-indigo));
border-radius: var(--radius);
display: flex; align-items: center; justify-content: center;
font-size: 22px;
color: #fff;
flex-shrink: 0;
box-shadow: 0 4px 12px rgba(88,166,255,.3);
}
.doc-title {
margin: 0;
font-size: 22px;
font-weight: 700;
color: var(--text-primary);
}
.doc-subtitle {
margin: 2px 0 0;
font-size: 13px;
color: var(--text-secondary);
}
/* ─── Navigation latérale ─── */
.doc-sidenav {
grid-area: sidenav;
background: var(--bg-surface);
border-right: 1px solid var(--border);
padding: 20px 0;
position: sticky;
top: 0;
height: 100vh;
overflow-y: auto;
}
.nav-section-label {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .08em;
color: var(--text-muted);
padding: 0 16px 8px;
margin: 0;
}
.nav-link {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 16px;
color: var(--text-secondary);
font-size: 13px;
transition: all .15s;
border-left: 2px solid transparent;
}
.nav-link:hover {
color: var(--text-primary);
background: var(--bg-card);
text-decoration: none;
}
.nav-link.active {
color: var(--accent-blue);
border-left-color: var(--accent-blue);
background: rgba(88,166,255,.08);
}
.nav-link.nav-sub {
padding-left: 28px;
font-size: 12.5px;
}
.nav-link.nav-sub-2 {
padding-left: 42px;
font-size: 12px;
}
/* ─── Contenu principal ─── */
.doc-content {
grid-area: content;
padding: 28px 32px;
max-width: 1100px;
}
/* ─── Sections ─── */
.doc-section {
margin-bottom: 48px;
}
.subsection {
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid var(--border);
}
.sub-subsection {
margin-top: 24px;
}
.section-header {
margin-bottom: 20px;
}
.section-header h2 {
margin: 6px 0 6px;
font-size: 20px;
font-weight: 700;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 10px;
}
.section-desc {
margin: 0;
color: var(--text-secondary);
font-size: 13px;
}
.subsection-title {
font-size: 16px;
font-weight: 600;
margin: 0 0 14px;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 8px;
}
.table-title {
font-size: 14px;
font-weight: 600;
margin: 0 0 12px;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.table-desc {
color: var(--text-secondary);
font-weight: 400;
font-size: 12.5px;
}
/* ─── Badges de section ─── */
.section-badge {
display: inline-block;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .06em;
padding: 2px 8px;
border-radius: 20px;
margin-bottom: 6px;
}
.badge-time { background: rgba(57,211,83,.12); color: var(--accent-green); border: 1px solid rgba(57,211,83,.3); }
.badge-orange { background: rgba(240,136,62,.12); color: var(--accent-orange); border: 1px solid rgba(240,136,62,.3);}
/* ─── Ligne de cartes ─── */
.info-row {
display: flex;
gap: 14px;
flex-wrap: wrap;
margin-bottom: 14px;
}
/* ─── Cartes d'information ─── */
.info-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 16px;
display: flex;
gap: 14px;
flex: 1;
min-width: 220px;
transition: background .15s, box-shadow .15s;
border-left-width: 3px;
}
.info-card:hover {
background: var(--bg-card-hover);
box-shadow: var(--shadow);
}
.info-card.full-width { flex: 1 1 100%; }
.card-icon {
width: 36px; height: 36px;
border-radius: var(--radius-sm);
display: flex; align-items: center; justify-content: center;
font-size: 16px;
flex-shrink: 0;
opacity: .85;
}
.card-body { flex: 1; min-width: 0; }
.card-body h4 {
margin: 0 0 6px;
font-size: 13.5px;
font-weight: 600;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.card-body p {
margin: 0 0 8px;
color: var(--text-secondary);
font-size: 13px;
}
/* Accents colorés des cartes */
.card-accent-blue { border-left-color: var(--accent-blue); }
.card-accent-blue .card-icon { background: rgba(88,166,255,.12); color: var(--accent-blue); }
.card-accent-cyan { border-left-color: var(--accent-cyan); }
.card-accent-cyan .card-icon { background: rgba(57,211,83,.12); color: var(--accent-cyan); }
.card-accent-green { border-left-color: var(--accent-green); }
.card-accent-green .card-icon { background: rgba(63,185,80,.12); color: var(--accent-green); }
.card-accent-teal { border-left-color: var(--accent-teal); }
.card-accent-teal .card-icon { background: rgba(45,212,191,.12); color: var(--accent-teal); }
.card-accent-orange { border-left-color: var(--accent-orange); }
.card-accent-orange .card-icon { background: rgba(240,136,62,.12); color: var(--accent-orange); }
.card-accent-yellow { border-left-color: var(--accent-yellow); }
.card-accent-yellow .card-icon { background: rgba(227,179,65,.12); color: var(--accent-yellow); }
.card-accent-red { border-left-color: var(--accent-red); }
.card-accent-red .card-icon { background: rgba(248,81,73,.12); color: var(--accent-red); }
.card-accent-purple { border-left-color: var(--accent-purple); }
.card-accent-purple .card-icon { background: rgba(188,140,255,.12);color: var(--accent-purple); }
.card-accent-indigo { border-left-color: var(--accent-indigo); }
.card-accent-indigo .card-icon { background: rgba(129,140,248,.12);color: var(--accent-indigo); }
/* ─── Lien d'accès ─── */
.access-link {
font-family: var(--font-mono);
font-size: 12.5px;
color: var(--accent-blue);
word-break: break-all;
}
.access-link .fa-external-link-alt { font-size: 10px; margin-left: 4px; }
/* ─── Listes documentaires ─── */
.doc-list {
display: flex;
flex-direction: column;
gap: 6px;
margin: 0;
padding: 0;
}
.doc-list li {
display: flex;
align-items: flex-start;
gap: 8px;
color: var(--text-secondary);
font-size: 13px;
padding: 4px 0;
border-bottom: 1px solid rgba(48,54,61,.5);
}
.doc-list li:last-child { border-bottom: none; }
.doc-list li i {
width: 16px;
text-align: center;
margin-top: 2px;
flex-shrink: 0;
}
/* ─── Tags ─── */
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
.tag-list.inline {
display: inline-flex;
margin-top: 0;
margin-left: 4px;
}
.tag {
font-size: 11px;
padding: 2px 8px;
border-radius: 20px;
font-weight: 600;
}
.tag-blue { background: rgba(88,166,255,.12); color: var(--accent-blue); border: 1px solid rgba(88,166,255,.2); }
.tag-green { background: rgba(63,185,80,.12); color: var(--accent-green); border: 1px solid rgba(63,185,80,.2); }
.tag-orange { background: rgba(240,136,62,.12); color: var(--accent-orange); border: 1px solid rgba(240,136,62,.2);}
.tag-gray { background: rgba(110,118,129,.12); color: var(--text-secondary);border: 1px solid rgba(110,118,129,.2);}
/* ─── Grille de puits ─── */
.well-grid {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
}
/* Grille serpentin : 6 colonnes fixes, 4 lignes (D/C/B/A) */
.well-grid--serpentine {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 5px;
margin-top: 12px;
}
.well-chip {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 700;
padding: 5px 4px;
border-radius: var(--radius-sm);
text-align: center;
letter-spacing: .03em;
/* couleur par défaut (teal) */
background: rgba(45,212,191,.1);
border: 1px solid rgba(45,212,191,.25);
color: var(--accent-teal);
}
/* Couleurs distinctes par ligne */
.wc-d {
background: rgba(248,81,73,.10);
border-color: rgba(248,81,73,.25);
color: var(--accent-red);
}
.wc-c {
background: rgba(240,136,62,.10);
border-color: rgba(240,136,62,.25);
color: var(--accent-orange);
}
.wc-b {
background: rgba(88,166,255,.10);
border-color: rgba(88,166,255,.25);
color: var(--accent-blue);
}
.wc-a {
background: rgba(63,185,80,.10);
border-color: rgba(63,185,80,.25);
color: var(--accent-green);
}
/* ─── Grille de position multi-puits ─── */
.position-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin: 10px 0;
}
.pos-cell {
background: rgba(129,140,248,.08);
border: 1px solid rgba(129,140,248,.2);
border-radius: var(--radius-sm);
padding: 8px 10px;
text-align: center;
font-weight: 700;
font-size: 13px;
color: var(--accent-indigo);
transition: background .15s;
}
.pos-cell.active:hover {
background: rgba(129,140,248,.18);
}
.pos-cell small {
display: block;
font-weight: 400;
font-size: 11px;
color: var(--text-secondary);
}
/* ─── Badges inline ─── */
.badge-auto {
font-size: 10px;
background: rgba(63,185,80,.12);
color: var(--accent-green);
border: 1px solid rgba(63,185,80,.25);
padding: 1px 6px;
border-radius: 20px;
margin-left: 4px;
font-weight: 600;
}
.badge-important {
font-size: 10px;
background: rgba(248,81,73,.15);
color: var(--accent-red);
border: 1px solid rgba(248,81,73,.3);
padding: 1px 6px;
border-radius: 20px;
margin-left: 4px;
font-weight: 700;
}
/* ─── Boîte d'alerte ─── */
.alert-box {
margin-top: 10px;
background: rgba(248,81,73,.08);
border: 1px solid rgba(248,81,73,.25);
border-radius: var(--radius-sm);
padding: 8px 12px;
color: var(--accent-red);
font-size: 12.5px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
/* ─── Description de tâche ─── */
.task-desc {
margin: 3px 0 0;
color: var(--text-muted);
font-size: 12px;
}
/* ─── Aide contextuelle ─── */
.hint {
margin: 8px 0 0;
color: var(--text-muted);
font-size: 12px;
}
/* ─── Code inline ─── */
code {
font-family: var(--font-mono);
font-size: 12.5px;
color: var(--text-code);
background: rgba(88,166,255,.07);
padding: 1px 5px;
border-radius: 3px;
border: 1px solid rgba(88,166,255,.12);
}
/* ─── Couleurs utilitaires ─── */
.text-gold { color: #d4a017; }
.text-blue { color: var(--accent-blue); }
.text-gray { color: var(--text-muted); }
.text-green { color: var(--accent-green); }
.text-cyan { color: var(--accent-cyan); }
/* ─── Scrollbar personnalisée ─── */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--bg-base); }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
/* ─── Responsive ─── */
@media (max-width: 768px) {
.doc-wrapper {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"content";
}
.doc-sidenav { display: none; }
.doc-content { padding: 16px; }
.info-card { min-width: 100%; }
}
@@ -0,0 +1,510 @@
{% extends 'scanner/base.html' %}
{% load i18n %}
{% block columns %}{% endblock %}
{% block styles %}
{{ block.super }}
<link href="/static/css/doc_database.css" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="doc-wrapper">
<!-- En-tête de la documentation -->
<header class="doc-header">
<div class="doc-header-inner">
<div class="doc-header-icon">
<i class="fas fa-database"></i>
</div>
<div>
<h1 class="doc-title">{% trans "Architecture des bases de données" %}</h1>
<p class="doc-subtitle">{% trans "Planarian Scanner — Infrastructure de stockage et modèles de données" %}</p>
</div>
</div>
</header>
<!-- Navigation latérale -->
<nav class="doc-sidenav" id="sidenav">
<p class="nav-section-label">{% trans "Navigation" %}</p>
<a href="#section-reductstore" class="nav-link active">
<i class="fas fa-wave-square"></i> ReductStore
</a>
<a href="#section-mariadb" class="nav-link">
<i class="fas fa-server"></i> MariaDB
</a>
<a href="#section-auth" class="nav-link nav-sub">
<i class="fas fa-shield-alt"></i> {% trans "Authentification" %}
</a>
<a href="#section-planarian" class="nav-link nav-sub">
<i class="fas fa-microscope"></i> Planarian
</a>
<a href="#section-scanner" class="nav-link nav-sub">
<i class="fas fa-camera"></i> Scanner
</a>
<a href="#section-config" class="nav-link nav-sub-2">
<i class="fas fa-sliders-h"></i> {% trans "Configuration" %}
</a>
<a href="#section-wells" class="nav-link nav-sub-2">
<i class="fas fa-circle"></i> {% trans "Puits" %}
</a>
<a href="#section-multiwell" class="nav-link nav-sub-2">
<i class="fas fa-th"></i> {% trans "Multi-puits" %}
</a>
<a href="#section-wellpos" class="nav-link nav-sub-2">
<i class="fas fa-map-marker-alt"></i> {% trans "Positions" %}
</a>
<a href="#section-experiments" class="nav-link nav-sub-2">
<i class="fas fa-flask"></i> {% trans "Expériences" %}
</a>
<a href="#section-sessions" class="nav-link nav-sub-2">
<i class="fas fa-play-circle"></i> {% trans "Sessions" %}
</a>
<a href="#section-celery" class="nav-link nav-sub-2">
<i class="fas fa-tasks"></i> Celery Beat
</a>
</nav>
<!-- Contenu principal -->
<main class="doc-content">
<!-- ============================================================ -->
<!-- SECTION 1 : ReductStore -->
<!-- ============================================================ -->
<section class="doc-section" id="section-reductstore">
<div class="section-header">
<span class="section-badge badge-time">{% trans "Séries temporelles" %}</span>
<h2><i class="fas fa-wave-square"></i> ReductStore</h2>
<p class="section-desc">{% trans "Base de données de séries temporelles pour les images et métriques de l'application." %}</p>
</div>
<div class="info-row">
<div class="info-card card-accent-blue">
<div class="card-icon"><i class="fas fa-link"></i></div>
<div class="card-body">
<h4>{% trans "Accès" %}</h4>
<a href="http://192.168.1.200:8383/ui/dashboard" target="_blank" class="access-link">
192.168.1.200:8383/ui/dashboard <i class="fas fa-external-link-alt"></i>
</a>
</div>
</div>
</div>
<h3 class="subsection-title"><i class="fas fa-bucket"></i> {% trans "Buckets" %}</h3>
<div class="info-row">
<div class="info-card card-accent-cyan">
<div class="card-icon"><i class="fas fa-images"></i></div>
<div class="card-body">
<h4><code>camera</code></h4>
<p>{% trans "Stockage des images capturées par la caméra." %}</p>
<div class="tag-list">
<span class="tag tag-blue">JPEG</span>
<span class="tag tag-gray">{% trans "Séries temporelles" %}</span>
</div>
</div>
</div>
<div class="info-card card-accent-cyan">
<div class="card-icon"><i class="fas fa-chart-line"></i></div>
<div class="card-body">
<h4><code>planarian_metrics</code></h4>
<p>{% trans "Métriques de suivi des planaires." %}</p>
<div class="tag-list">
<span class="tag tag-green">JSON</span>
<span class="tag tag-gray">{% trans "Séries temporelles" %}</span>
</div>
</div>
</div>
</div>
</section>
<!-- ============================================================ -->
<!-- SECTION 2 : MariaDB -->
<!-- ============================================================ -->
<section class="doc-section" id="section-mariadb">
<div class="section-header">
<span class="section-badge badge-orange">{% trans "Base relationnelle" %}</span>
<h2><i class="fas fa-server"></i> MariaDB</h2>
<p class="section-desc">{% trans "Base de données relationnelle principale de l'application Django." %}</p>
</div>
<div class="info-row">
<div class="info-card card-accent-orange">
<div class="card-icon"><i class="fas fa-database"></i></div>
<div class="card-body">
<h4>Adminer</h4>
<a href="http://192.168.1.200/adminer/" target="_blank" class="access-link">
192.168.1.200/adminer/ <i class="fas fa-external-link-alt"></i>
</a>
</div>
</div>
<div class="info-card card-accent-orange">
<div class="card-icon"><i class="fas fa-user-shield"></i></div>
<div class="card-body">
<h4>{% trans "Administration Django" %}</h4>
<a href="http://192.168.1.200/admin" target="_blank" class="access-link">
192.168.1.200/admin <i class="fas fa-external-link-alt"></i>
</a>
</div>
</div>
</div>
<!-- -------------------------------------------------------- -->
<!-- Authentification -->
<!-- -------------------------------------------------------- -->
<div class="subsection" id="section-auth">
<h3 class="subsection-title"><i class="fas fa-shield-alt"></i> {% trans "Authentification & Autorisation" %}</h3>
<div class="info-row">
<div class="info-card card-accent-purple">
<div class="card-icon"><i class="fas fa-users-cog"></i></div>
<div class="card-body">
<h4>{% trans "Groupes" %}</h4>
<ul class="doc-list">
<li><i class="fas fa-crown text-gold"></i> Admin</li>
<li><i class="fas fa-user-tie text-blue"></i> Staff</li>
<li><i class="fas fa-user text-gray"></i> {% trans "Staff limité" %}</li>
</ul>
</div>
</div>
<div class="info-card card-accent-purple">
<div class="card-icon"><i class="fas fa-user-circle"></i></div>
<div class="card-body">
<h4>{% trans "Utilisateurs" %}</h4>
<ul class="doc-list">
<li><i class="fas fa-star text-gold"></i> superadmin</li>
</ul>
</div>
</div>
</div>
</div>
<!-- -------------------------------------------------------- -->
<!-- Planarian -->
<!-- -------------------------------------------------------- -->
<div class="subsection" id="section-planarian">
<h3 class="subsection-title"><i class="fas fa-microscope"></i> {% trans "Application Planarian" %}</h3>
<div class="info-row">
<div class="info-card card-accent-green full-width">
<div class="card-icon"><i class="fas fa-vials"></i></div>
<div class="card-body">
<h4><code>planarian_experimentconfig</code></h4>
<p>{% trans "Configurations des expériences de suivi des planaires." %}</p>
</div>
</div>
</div>
</div>
<!-- -------------------------------------------------------- -->
<!-- Scanner -->
<!-- -------------------------------------------------------- -->
<div class="subsection" id="section-scanner">
<h3 class="subsection-title"><i class="fas fa-camera"></i> {% trans "Application Scanner" %}</h3>
<!-- Configuration -->
<div class="sub-subsection" id="section-config">
<h4 class="table-title">
<i class="fas fa-sliders-h"></i>
<code>scanner_configuration</code>
<span class="table-desc">{% trans "Constantes et valeurs par défaut" %}</span>
</h4>
<div class="info-row">
<div class="info-card card-accent-blue">
<div class="card-icon"><i class="fas fa-camera-retro"></i></div>
<div class="card-body">
<h4>{% trans "Caméra" %}</h4>
<ul class="doc-list">
<li>
<i class="fas fa-plug"></i>
{% trans "Capture" %} :
<div class="tag-list inline">
<span class="tag tag-blue">Arducam</span>
<span class="tag tag-gray">Webcam</span>
<span class="tag tag-gray">{% trans "Fichier vidéo" %}</span>
</div>
</li>
<li><i class="fas fa-film"></i> {% trans "Fréquence vidéos (fps)" %}</li>
<li><i class="fas fa-arrows-alt-h"></i> {% trans "Largeur de capture vidéo" %}</li>
<li><i class="fas fa-arrows-alt-v"></i> {% trans "Hauteur de capture vidéo" %}</li>
</ul>
</div>
</div>
<div class="info-card card-accent-blue">
<div class="card-icon"><i class="fas fa-crosshairs"></i></div>
<div class="card-body">
<h4>{% trans "Suivi / Tracking" %}</h4>
<ul class="doc-list">
<li><i class="fas fa-cog"></i> {% trans "Valeurs par défaut du tracking" %}</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Puits -->
<div class="sub-subsection" id="section-wells">
<h4 class="table-title">
<i class="fas fa-circle"></i>
<code>scanner_well</code>
<span class="table-desc">{% trans "Noms des puits" %}</span>
</h4>
<div class="info-row">
<div class="info-card card-accent-teal full-width">
<div class="card-icon"><i class="fas fa-th-large"></i></div>
<div class="card-body">
<p>{% trans "Contient uniquement les noms des puits disponibles." %}</p>
<p class="hint"><i class="fas fa-exchange-alt"></i> {% trans "Lecture en serpentin dans le sens des ±X" %}</p>
<!-- Grille serpentin : D1→D6 / C6←C1 / B1→B6 / A6←A1 -->
<div class="well-grid well-grid--serpentine">
{# Ligne D : gauche → droite (D1..D6) #}
<span class="well-chip wc-d">D1</span><span class="well-chip wc-d">D2</span>
<span class="well-chip wc-d">D3</span><span class="well-chip wc-d">D4</span>
<span class="well-chip wc-d">D5</span><span class="well-chip wc-d">D6</span>
{# Ligne C : droite → gauche (C6..C1) #}
<span class="well-chip wc-c">C6</span><span class="well-chip wc-c">C5</span>
<span class="well-chip wc-c">C4</span><span class="well-chip wc-c">C3</span>
<span class="well-chip wc-c">C2</span><span class="well-chip wc-c">C1</span>
{# Ligne B : gauche → droite (B1..B6) #}
<span class="well-chip wc-b">B1</span><span class="well-chip wc-b">B2</span>
<span class="well-chip wc-b">B3</span><span class="well-chip wc-b">B4</span>
<span class="well-chip wc-b">B5</span><span class="well-chip wc-b">B6</span>
{# Ligne A : droite → gauche (A6..A1) #}
<span class="well-chip wc-a">A6</span><span class="well-chip wc-a">A5</span>
<span class="well-chip wc-a">A4</span><span class="well-chip wc-a">A3</span>
<span class="well-chip wc-a">A2</span><span class="well-chip wc-a">A1</span>
</div>
</div>
</div>
</div>
</div>
<!-- Multi-puits -->
<div class="sub-subsection" id="section-multiwell">
<h4 class="table-title">
<i class="fas fa-th"></i>
<code>scanner_multiwell</code>
<span class="table-desc">{% trans "Géométrie et déplacements" %}</span>
</h4>
<div class="info-row">
<div class="info-card card-accent-indigo">
<div class="card-icon"><i class="fas fa-map"></i></div>
<div class="card-body">
<h4>{% trans "Position sur la table" %}</h4>
<div class="position-grid">
<div class="pos-cell active">HG<br><small>{% trans "Haut gauche" %}</small></div>
<div class="pos-cell active">HD<br><small>{% trans "Haut droit" %}</small></div>
<div class="pos-cell active">BG<br><small>{% trans "Bas gauche" %}</small></div>
<div class="pos-cell active">BD<br><small>{% trans "Bas droit" %}</small></div>
</div>
<p class="hint"><i class="fas fa-info-circle"></i> {% trans "4 positions disponibles sur la table" %}</p>
<p class="hint"><i class="fas fa-info-circle"></i> {% trans "(0, 0) de le CNC est en HD" %}</p>
<p class="hint"><i class="fas fa-info-circle"></i> {% trans "HD --> HG +X" %}</p>
<p class="hint"><i class="fas fa-info-circle"></i> {% trans "HD --> BD +Y" %}</p>
</div>
</div>
<div class="info-card card-accent-indigo">
<div class="card-icon"><i class="fas fa-ruler-combined"></i></div>
<div class="card-body">
<h4>{% trans "Géométrie" %}</h4>
<ul class="doc-list">
<li><i class="fas fa-grip-lines"></i> {% trans "Nombre de lignes" %}</li>
<li><i class="fas fa-grip-lines-vertical"></i> {% trans "Nombre de colonnes" %}</li>
<li><i class="fas fa-circle"></i> {% trans "Diamètre des puits" %}</li>
<li><i class="fas fa-font"></i> {% trans "Définition des lignes (A, B, C, D)" %}</li>
<li><i class="fas fa-exchange-alt"></i> {% trans "Lecture en serpentin ±X (D→C→B→A)" %}</li>
</ul>
</div>
</div>
<div class="info-card card-accent-indigo">
<div class="card-icon"><i class="fas fa-route"></i></div>
<div class="card-body">
<h4>{% trans "Déplacements" %}</h4>
<ul class="doc-list">
<li><i class="fas fa-sort-numeric-down"></i> {% trans "Ordre de lecture" %}</li>
<li><i class="fas fa-stopwatch"></i> {% trans "Durée de capture (calibration)" %}</li>
<li>
<i class="fas fa-location-arrow"></i>
{% trans "Origine X" %}
<span class="badge-auto">{% trans "Calibration" %}</span>
</li>
<li>
<i class="fas fa-location-arrow fa-rotate-90"></i>
{% trans "Origine Y" %}
<span class="badge-auto">{% trans "Calibration" %}</span>
</li>
<li><i class="fas fa-arrows-alt-h"></i> {% trans "Pas X mesuré" %}</li>
<li><i class="fas fa-arrows-alt-v"></i> {% trans "Pas Y mesuré" %}</li>
<li><i class="fas fa-tachometer-alt"></i> {% trans "Vitesse (mm/mn)" %}</li>
</ul>
</div>
</div>
</div>
<div class="info-row">
<div class="info-card card-accent-yellow full-width">
<div class="card-icon"><i class="fas fa-exclamation-triangle"></i></div>
<div class="card-body">
<h4>{% trans "Positions générées" %}</h4>
<p>{% trans "Si les positions ne sont pas encore générées, le système efface" %} <code>WellPosition</code> {% trans "et recalcule toutes les positions des puits." %}</p>
</div>
</div>
</div>
</div>
<!-- Position des puits -->
<div class="sub-subsection" id="section-wellpos">
<h4 class="table-title">
<i class="fas fa-map-marker-alt"></i>
<code>scanner_wellposition</code>
<span class="table-desc">{% trans "Positions calibrées des puits" %}</span>
</h4>
<div class="info-row">
<div class="info-card card-accent-cyan">
<div class="card-icon"><i class="fas fa-drafting-compass"></i></div>
<div class="card-body">
<h4>{% trans "Calibration" %}</h4>
<p>{% trans "Manuelle ou automatique." %}</p>
<ul class="doc-list">
<li><i class="fas fa-arrows-alt-h"></i> {% trans "Axe X (mm)" %} <span class="badge-auto">{% trans "Calibration" %}</span></li>
<li><i class="fas fa-arrows-alt-v"></i> {% trans "Axe Y (mm)" %} <span class="badge-auto">{% trans "Calibration" %}</span></li>
</ul>
</div>
</div>
<div class="info-card card-accent-red">
<div class="card-icon"><i class="fas fa-exclamation-circle"></i></div>
<div class="card-body">
<h4>{% trans "Pixels par mm" %} <span class="badge-important">{% trans "IMPORTANT" %}</span></h4>
<p>{% trans "Facteur de calibration optique calculé en fonction de la taille de l'image." %}</p>
<div class="alert-box">
<i class="fas fa-redo"></i>
{% trans "Recalibrer si la taille de l'image est modifiée !" %}
</div>
</div>
</div>
</div>
</div>
<!-- Expériences -->
<div class="sub-subsection" id="section-experiments">
<h4 class="table-title">
<i class="fas fa-flask"></i>
<code>scanner_experiment</code> &amp; <code>scanner_experimentwell</code>
<span class="table-desc">{% trans "Expériences et puits associés" %}</span>
</h4>
<div class="info-row">
<div class="info-card card-accent-green">
<div class="card-icon"><i class="fas fa-vial"></i></div>
<div class="card-body">
<h4>{% trans "Valeurs importantes" %}</h4>
<ul class="doc-list">
<li><i class="fas fa-link"></i> {% trans "Multi-puits lié" %}</li>
<li><i class="fas fa-clock"></i> {% trans "Durée de capture (secondes)" %}</li>
<li><i class="fas fa-fingerprint"></i> {% trans "Identifiant d'expérience" %}</li>
</ul>
</div>
</div>
<div class="info-card card-accent-green">
<div class="card-icon"><i class="fas fa-magic"></i></div>
<div class="card-body">
<h4>{% trans "Création automatique" %}</h4>
<p>{% trans "À la création d'une expérience, les puits liés sont automatiquement créés dans" %} <code>scanner_experimentwell</code>.</p>
</div>
</div>
</div>
</div>
<!-- Sessions -->
<div class="sub-subsection" id="section-sessions">
<h4 class="table-title">
<i class="fas fa-play-circle"></i>
<code>scanner_session</code> &amp; <code>scanner_sessionexperiment</code>
<span class="table-desc">{% trans "Sessions d'expériences" %}</span>
</h4>
<div class="info-row">
<div class="info-card card-accent-blue">
<div class="card-icon"><i class="fas fa-layer-group"></i></div>
<div class="card-body">
<h4>{% trans "Regroupement" %}</h4>
<p>{% trans "Regroupe les expériences lors d'un balayage dans" %} <code>scanner_sessionexperiment</code>.</p>
<div class="tag-list">
<span class="tag tag-blue">{% trans "4 multi-puits maximum" %}</span>
</div>
</div>
</div>
<div class="info-card card-accent-blue">
<div class="card-icon"><i class="fas fa-tasks"></i></div>
<div class="card-body">
<h4>{% trans "2 tâches Celery créées" %}</h4>
<ul class="doc-list">
<li>
<i class="fas fa-play text-green"></i>
<code>scanning_session</code>
<p class="task-desc">{% trans "Lancement différé d'un balayage de session." %}</p>
</li>
<li>
<i class="fas fa-file-export text-cyan"></i>
<code>export_session</code>
<p class="task-desc">
{% trans "Export images et vidéos :" %}
<span class="tag tag-gray">{% trans "Local" %}</span>
<span class="tag tag-gray">{% trans "Support distant" %}</span>
</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Celery Beat -->
<div class="sub-subsection" id="section-celery">
<h4 class="table-title">
<i class="fas fa-tasks"></i>
<code>django_celery_beat_periodictask</code>
<span class="table-desc">{% trans "Gestion des tâches différées" %}</span>
</h4>
<div class="info-row">
<div class="info-card card-accent-orange full-width">
<div class="card-icon"><i class="fas fa-clock"></i></div>
<div class="card-body">
<p>{% trans "Gère le déclenchement et la planification des tâches Celery différées." %}</p>
<div class="tag-list">
<span class="tag tag-orange">Celery Beat</span>
<span class="tag tag-gray">{% trans "Tâches périodiques" %}</span>
</div>
</div>
</div>
</div>
</div>
</div><!-- end section-scanner -->
</section><!-- end section-mariadb -->
</main><!-- end doc-content -->
</div><!-- end doc-wrapper -->
{% endblock %}
{% block js_footer %}
{{ block.super }}
<script>
/* Mise en évidence du lien actif dans la navigation latérale */
(function () {
const links = document.querySelectorAll('.nav-link');
const sections = document.querySelectorAll('.doc-section, .subsection, .sub-subsection');
/* Observer d'intersection pour la navigation active */
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
links.forEach(l => l.classList.remove('active'));
const target = document.querySelector(`.nav-link[href="#${entry.target.id}"]`);
if (target) target.classList.add('active');
}
});
},
{ rootMargin: '-10% 0px -80% 0px' }
);
sections.forEach(s => { if (s.id) observer.observe(s); });
})();
</script>
{% endblock %}
+1 -1
View File
@@ -121,7 +121,7 @@ class MultiWell(models.Model):
row_order = models.CharField(_("Ordre ligne"), help_text=_('Ordre ligne de puit. Lecture en serpentin dans le sens des +- X'), max_length=16, null=True, blank=False, default="D,C,B,A") row_order = models.CharField(_("Ordre ligne"), help_text=_('Ordre ligne de puit. Lecture en serpentin dans le sens des +- X'), max_length=16, null=True, blank=False, default="D,C,B,A")
# Balayage # Balayage
order = models.PositiveSmallIntegerField(_("Ordre"), help_text=_('Ordre de lecture du multi-puit'), blank=False, default=0) order = models.PositiveSmallIntegerField(_("Ordre"), help_text=_('Ordre de lecture du multi-puit'), blank=False, default=0)
duration = models.PositiveIntegerField(_("Durée"), help_text=_('Durée du film en secondes por la calibration'), blank=False, default=10) duration = models.PositiveIntegerField(_("Durée"), help_text=_('Durée de capture en secondes pour la calibration'), blank=False, default=10)
xbase = models.FloatField(_("Origine X"), help_text=_('Base origine X en mm'), blank=False, default=50.0) xbase = models.FloatField(_("Origine X"), help_text=_('Base origine X en mm'), blank=False, default=50.0)
ybase = models.FloatField(_("Origine Y"), help_text=_('Base origine Y en mm'), blank=False, default=50.0) ybase = models.FloatField(_("Origine Y"), help_text=_('Base origine Y en mm'), blank=False, default=50.0)
@@ -38,6 +38,10 @@
</a> </a>
{% endif %} {% endif %}
<hr class="w3-bar-item divider w3-grey"> <hr class="w3-bar-item divider w3-grey">
<a href="{% url 'scanner:documentation' 'doc_database' %}" class="w3-bar-item w3-btn w3-hover-opacity">
<i class="fa-solid fa-book w3-text-green"></i> {% trans "Descriptif des données" %}
</a>
<form method="post" action="{% url 'logout' %}"> <form method="post" action="{% url 'logout' %}">
{% csrf_token %} {% csrf_token %}
<button class="w3-bar-item w3-btn w3-hover-opacity" type="submit"> <button class="w3-bar-item w3-btn w3-hover-opacity" type="submit">
@@ -99,17 +103,17 @@
<i class="fa-solid fa-gear w3-text-purple w3-xlarge"></i> {% trans "Création de sessions d'expériences" %} <i class="fa-solid fa-gear w3-text-purple w3-xlarge"></i> {% trans "Création de sessions d'expériences" %}
</a> </a>
<hr class="w3-bar-item divider w3-grey"> <hr class="w3-bar-item divider w3-grey">
{% if conf.tracking %}
<a href="{% url 'planarian:import-params' %}" class="w3-bar-item w3-btn w3-hover-opacity">
<i class="fa-solid fa-file-import w3-text-lime w3-xlarge"></i> {% trans "Importer des configurations depuis CSV" %}
</a>
{% endif %}
<a href="{% url 'scanner:experiment_view' %}" class="w3-bar-item w3-btn w3-hover-opacity"> <a href="{% url 'scanner:experiment_view' %}" class="w3-bar-item w3-btn w3-hover-opacity">
<i class="fa-solid fa-gear w3-text-pink w3-xlarge"></i> {% trans "Gestions des expériences" %} <i class="fa-solid fa-gear w3-text-pink w3-xlarge"></i> {% trans "Gestions des expériences" %}
</a> </a>
<a href="{% url 'scanner:experimentconfig_view' %}" class="w3-bar-item w3-btn w3-hover-opacity"> <a href="{% url 'scanner:experimentconfig_view' %}" class="w3-bar-item w3-btn w3-hover-opacity">
<i class="fa-solid fa-gears w3-text-pink w3-xlarge"></i> {% trans "Configuration des expériences" %} <i class="fa-solid fa-gears w3-text-pink w3-xlarge"></i> {% trans "Configuration des expériences" %}
</a> </a>
{% if conf.tracking %}
<a href="{% url 'planarian:import-params' %}" class="w3-bar-item w3-btn w3-hover-opacity">
<i class="fa-solid fa-file-import w3-text-lime w3-xlarge"></i> {% trans "Importer des configurations depuis CSV" %}
</a>
{% endif %}
<a href="{% url 'scanner:calibration' %}" class="w3-bar-item w3-btn w3-hover-opacity"> <a href="{% url 'scanner:calibration' %}" class="w3-bar-item w3-btn w3-hover-opacity">
<i class="fa-solid fa-wrench w3-text-orange w3-xlarge"></i> {% trans "Calibration" %} <i class="fa-solid fa-wrench w3-text-orange w3-xlarge"></i> {% trans "Calibration" %}
</a> </a>
@@ -118,7 +122,7 @@
<a href="{% url 'scanner:scanning' %}" class="w3-bar-item w3-btn w3-hover-opacity"> <a href="{% url 'scanner:scanning' %}" class="w3-bar-item w3-btn w3-hover-opacity">
<i class="fa-solid fa-film w3-text-green w3-xlarge""></i> {% trans "Balayage multi-puits" %} <i class="fa-solid fa-film w3-text-green w3-xlarge""></i> {% trans "Balayage multi-puits" %}
</a> </a>
<div class="w3-bar-item w3-dark-xxlight">{% trans "Résultats " %}</div> <div class="w3-bar-item w3-light-blue">{% trans "Résultats " %}</div>
{% if conf.tracking %} {% if conf.tracking %}
<a href="{% url 'planarian:export-csv' %}" class="w3-bar-item w3-btn w3-hover-opacity"> <a href="{% url 'planarian:export-csv' %}" class="w3-bar-item w3-btn w3-hover-opacity">
<i class="fa-solid fa-file-export w3-text-lime w3-xlarge"></i> {% trans "Export CSV des expériences" %} <i class="fa-solid fa-file-export w3-text-lime w3-xlarge"></i> {% trans "Export CSV des expériences" %}
+1
View File
@@ -13,6 +13,7 @@ urlpatterns = [
path('supervisor/', views.supervisor_view, name='supervisor'), path('supervisor/', views.supervisor_view, name='supervisor'),
path('logs/worker', views.supervisor_worker, name='logs_worker'), path('logs/worker', views.supervisor_worker, name='logs_worker'),
path('logs/scheduler', views.supervisor_scheduler, name='logs_scheduler'), path('logs/scheduler', views.supervisor_scheduler, name='logs_scheduler'),
path("doc/<slug:template>/", views.documentation, name="documentation"),
path('session/view', views.admin_session_view, name='session_view'), path('session/view', views.admin_session_view, name='session_view'),
path('experiment/view', views.admin_experiment_view, name='experiment_view'), path('experiment/view', views.admin_experiment_view, name='experiment_view'),
+8 -1
View File
@@ -51,6 +51,7 @@ def global_context(request, **ctx):
**ctx **ctx
) )
@login_required @login_required
@user_passes_test(is_staff_or_admin) @user_passes_test(is_staff_or_admin)
def admin_view(request): def admin_view(request):
@@ -76,7 +77,6 @@ def admin_periodictask_view(request):
@login_required @login_required
@user_passes_test(is_staff_or_admin) @user_passes_test(is_staff_or_admin)
def reductstore_view(request): def reductstore_view(request):
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:8383/')) return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:8383/'))
@login_required @login_required
@@ -104,6 +104,13 @@ def supervisor_worker(request):
def supervisor_scheduler(request): def supervisor_scheduler(request):
return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9001/logtail/test_tube:planification')) return render(request, "scanner/redirection.html", context=global_context(request, link=f'http://{settings.LOCAL_IP_SERVER}:9001/logtail/test_tube:planification'))
def documentation(request, template=None):
ctx = dict(
choice_title=_("Documentation")
)
return render(request, f"docs/{template}.html", context=global_context(request, **ctx))
## Mainboard ## Mainboard
@login_required @login_required
def scanning_view(request): def scanning_view(request):