diff --git a/test_tube_scanner/home/static/css/doc_database.css b/test_tube_scanner/home/static/css/doc_database.css new file mode 100644 index 0000000..17f8199 --- /dev/null +++ b/test_tube_scanner/home/static/css/doc_database.css @@ -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%; } +} diff --git a/test_tube_scanner/home/templates/docs/doc_database.html b/test_tube_scanner/home/templates/docs/doc_database.html new file mode 100644 index 0000000..6cc4d32 --- /dev/null +++ b/test_tube_scanner/home/templates/docs/doc_database.html @@ -0,0 +1,510 @@ +{% extends 'scanner/base.html' %} +{% load i18n %} + +{% block columns %}{% endblock %} + +{% block styles %} + {{ block.super }} + +{% endblock %} + +{% block content %} +
+ + +
+
+
+ +
+
+

{% trans "Architecture des bases de données" %}

+

{% trans "Planarian Scanner — Infrastructure de stockage et modèles de données" %}

+
+
+
+ + + + + +
+ + + + +
+
+ {% trans "Séries temporelles" %} +

ReductStore

+

{% trans "Base de données de séries temporelles pour les images et métriques de l'application." %}

+
+ +
+
+
+
+

{% trans "Accès" %}

+ + 192.168.1.200:8383/ui/dashboard + +
+
+
+ +

{% trans "Buckets" %}

+
+
+
+
+

camera

+

{% trans "Stockage des images capturées par la caméra." %}

+
+ JPEG + {% trans "Séries temporelles" %} +
+
+
+
+
+
+

planarian_metrics

+

{% trans "Métriques de suivi des planaires." %}

+
+ JSON + {% trans "Séries temporelles" %} +
+
+
+
+
+ + + + +
+
+ {% trans "Base relationnelle" %} +

MariaDB

+

{% trans "Base de données relationnelle principale de l'application Django." %}

+
+ +
+
+
+ +
+
+
+
+

{% trans "Administration Django" %}

+ + 192.168.1.200/admin + +
+
+
+ + + + +
+

{% trans "Authentification & Autorisation" %}

+ +
+
+
+
+

{% trans "Groupes" %}

+
    +
  • Admin
  • +
  • Staff
  • +
  • {% trans "Staff limité" %}
  • +
+
+
+
+
+
+

{% trans "Utilisateurs" %}

+
    +
  • superadmin
  • +
+
+
+
+
+ + + + +
+

{% trans "Application Planarian" %}

+
+
+
+
+

planarian_experimentconfig

+

{% trans "Configurations des expériences de suivi des planaires." %}

+
+
+
+
+ + + + +
+

{% trans "Application Scanner" %}

+ + +
+

+ + scanner_configuration + {% trans "Constantes et valeurs par défaut" %} +

+
+
+
+
+

{% trans "Caméra" %}

+
    +
  • + + {% trans "Capture" %} : +
    + Arducam + Webcam + {% trans "Fichier vidéo" %} +
    +
  • +
  • {% trans "Fréquence vidéos (fps)" %}
  • +
  • {% trans "Largeur de capture vidéo" %}
  • +
  • {% trans "Hauteur de capture vidéo" %}
  • +
+
+
+
+
+
+

{% trans "Suivi / Tracking" %}

+
    +
  • {% trans "Valeurs par défaut du tracking" %}
  • +
+
+
+
+
+ + +
+

+ + scanner_well + {% trans "Noms des puits" %} +

+
+
+
+
+

{% trans "Contient uniquement les noms des puits disponibles." %}

+

{% trans "Lecture en serpentin dans le sens des ±X" %}

+ +
+ {# Ligne D : gauche → droite (D1..D6) #} + D1D2 + D3D4 + D5D6 + {# Ligne C : droite → gauche (C6..C1) #} + C6C5 + C4C3 + C2C1 + {# Ligne B : gauche → droite (B1..B6) #} + B1B2 + B3B4 + B5B6 + {# Ligne A : droite → gauche (A6..A1) #} + A6A5 + A4A3 + A2A1 +
+
+
+
+
+ + +
+

+ + scanner_multiwell + {% trans "Géométrie et déplacements" %} +

+
+
+
+
+

{% trans "Position sur la table" %}

+
+
HG
{% trans "Haut gauche" %}
+
HD
{% trans "Haut droit" %}
+
BG
{% trans "Bas gauche" %}
+
BD
{% trans "Bas droit" %}
+
+

{% trans "4 positions disponibles sur la table" %}

+

{% trans "(0, 0) de le CNC est en HD" %}

+

{% trans "HD --> HG +X" %}

+

{% trans "HD --> BD +Y" %}

+
+
+
+
+
+

{% trans "Géométrie" %}

+
    +
  • {% trans "Nombre de lignes" %}
  • +
  • {% trans "Nombre de colonnes" %}
  • +
  • {% trans "Diamètre des puits" %}
  • +
  • {% trans "Définition des lignes (A, B, C, D)" %}
  • +
  • {% trans "Lecture en serpentin ±X (D→C→B→A)" %}
  • +
+
+
+
+
+
+

{% trans "Déplacements" %}

+
    +
  • {% trans "Ordre de lecture" %}
  • +
  • {% trans "Durée de capture (calibration)" %}
  • +
  • + + {% trans "Origine X" %} + {% trans "Calibration" %} +
  • +
  • + + {% trans "Origine Y" %} + {% trans "Calibration" %} +
  • +
  • {% trans "Pas X mesuré" %}
  • +
  • {% trans "Pas Y mesuré" %}
  • +
  • {% trans "Vitesse (mm/mn)" %}
  • +
+
+
+
+
+
+
+
+

{% trans "Positions générées" %}

+

{% trans "Si les positions ne sont pas encore générées, le système efface" %} WellPosition {% trans "et recalcule toutes les positions des puits." %}

+
+
+
+
+ + +
+

+ + scanner_wellposition + {% trans "Positions calibrées des puits" %} +

+
+
+
+
+

{% trans "Calibration" %}

+

{% trans "Manuelle ou automatique." %}

+
    +
  • {% trans "Axe X (mm)" %} {% trans "Calibration" %}
  • +
  • {% trans "Axe Y (mm)" %} {% trans "Calibration" %}
  • +
+
+
+
+
+
+

{% trans "Pixels par mm" %} {% trans "IMPORTANT" %}

+

{% trans "Facteur de calibration optique calculé en fonction de la taille de l'image." %}

+
+ + {% trans "Recalibrer si la taille de l'image est modifiée !" %} +
+
+
+
+
+ + +
+

+ + scanner_experiment & scanner_experimentwell + {% trans "Expériences et puits associés" %} +

+
+
+
+
+

{% trans "Valeurs importantes" %}

+
    +
  • {% trans "Multi-puits lié" %}
  • +
  • {% trans "Durée de capture (secondes)" %}
  • +
  • {% trans "Identifiant d'expérience" %}
  • +
+
+
+
+
+
+

{% trans "Création automatique" %}

+

{% trans "À la création d'une expérience, les puits liés sont automatiquement créés dans" %} scanner_experimentwell.

+
+
+
+
+ + +
+

+ + scanner_session & scanner_sessionexperiment + {% trans "Sessions d'expériences" %} +

+
+
+
+
+

{% trans "Regroupement" %}

+

{% trans "Regroupe les expériences lors d'un balayage dans" %} scanner_sessionexperiment.

+
+ {% trans "4 multi-puits maximum" %} +
+
+
+
+
+
+

{% trans "2 tâches Celery créées" %}

+
    +
  • + + scanning_session +

    {% trans "Lancement différé d'un balayage de session." %}

    +
  • +
  • + + export_session +

    + {% trans "Export images et vidéos :" %} + {% trans "Local" %} + {% trans "Support distant" %} +

    +
  • +
+
+
+
+
+ + +
+

+ + django_celery_beat_periodictask + {% trans "Gestion des tâches différées" %} +

+
+
+
+
+

{% trans "Gère le déclenchement et la planification des tâches Celery différées." %}

+
+ Celery Beat + {% trans "Tâches périodiques" %} +
+
+
+
+
+ +
+
+ +
+
+{% endblock %} + +{% block js_footer %} + {{ block.super }} + +{% endblock %} diff --git a/test_tube_scanner/scanner/models.py b/test_tube_scanner/scanner/models.py index 680e500..2c59b81 100644 --- a/test_tube_scanner/scanner/models.py +++ b/test_tube_scanner/scanner/models.py @@ -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") # Balayage 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) ybase = models.FloatField(_("Origine Y"), help_text=_('Base origine Y en mm'), blank=False, default=50.0) diff --git a/test_tube_scanner/scanner/templates/scanner/base.html b/test_tube_scanner/scanner/templates/scanner/base.html index ec18acc..ec1d7d2 100644 --- a/test_tube_scanner/scanner/templates/scanner/base.html +++ b/test_tube_scanner/scanner/templates/scanner/base.html @@ -38,6 +38,10 @@ {% endif %}
+ + + {% trans "Descriptif des données" %} +
{% csrf_token %}