# Generated by Django 6.0.5 on 2026-05-31 07:42 import django.db.models.deletion import django.utils.timezone from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ('django_celery_beat', '0019_alter_periodictasks_options'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='Configuration', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(default='Configuration par défaut', help_text='Nom de la configuration', max_length=100, null=True, verbose_name='Nom de la Configuration')), ('sidebar_width', models.CharField(default='350px', help_text='Largeur barre latérale (css)', max_length=32, null=True, verbose_name='Barre latérale')), ('default_grid_columns', models.PositiveSmallIntegerField(default=3, help_text='Nombre de colonnes de la grille par défaut', verbose_name='Colonnes de la grille par défaut')), ('opencv_fourcc_format', models.CharField(choices=[('mp4v', 'MP4'), ('XVID', 'XVID')], default='mp4v', help_text='Opencv fourcc format', max_length=8, null=True, verbose_name='Fourcc')), ('opencv_video_type', models.CharField(choices=[('mp4', 'MP4'), ('avi', 'AVI')], default='mp4', help_text='Opencv video type', max_length=8, null=True, verbose_name='Video type')), ('grbl_xmax', models.FloatField(default=350.0, help_text='CNC Grbl Xmax en mm', verbose_name='Grbl Xmax')), ('grbl_ymax', models.FloatField(default=250.0, help_text='CNC Grbl Ymax en mm', verbose_name='Grbl Ymax')), ('capture_type', models.CharField(choices=[('rpi', 'Arducam'), ('webcam', 'Webcam'), ('file', 'Simulation Fichier vidéo (mp4, avi)'), ('video', 'Fichier vidéo (mp4, avi)')], default='rpi', help_text='Type de capture. Nécessite un redémarrage en cas de modification à chaud!', max_length=8, null=True, verbose_name='Capture')), ('webcam_device_index', models.PositiveSmallIntegerField(default=2, help_text='Index de la webcam (0, 1, ...) si présente', verbose_name='Index de la webcam')), ('image_quality', models.PositiveSmallIntegerField(default=90, help_text='Qualité JPEG (1-100) pour les images exportées', verbose_name='Qualité JPEG')), ('video_jpeg_quality', models.PositiveSmallIntegerField(default=90, help_text='Qualité JPEG (1-100) pour les images extraites des vidéos', verbose_name='Qualité JPEG pour les vidéos')), ('video_frame_rate', models.FloatField(default=5.0, help_text="Fréquence d'extraction des images des vidéos (images par seconde)", verbose_name='Fréquence vidéos (fps)')), ('video_width_capture', models.PositiveSmallIntegerField(default=1280, help_text='Largeur de capture vidéo en pixels', verbose_name='Largeur de capture vidéo')), ('video_height_capture', models.PositiveSmallIntegerField(default=720, help_text='Hauteur de capture vidéo en pixels', verbose_name='Hauteur de capture vidéo')), ('scan_simulation', models.BooleanField(default=False, help_text='Autorise la simulation du balayage', verbose_name='Simuler balayage')), ('calibration_crop_radius', models.PositiveSmallIntegerField(default=150, help_text='Rayon en pixels pour découper les images de calibration en px', verbose_name='Rayon de découpe pour la calibration')), ('calibration_default_multiwell', models.CharField(choices=[('HG', 'HG-Haut gauche'), ('HD', 'HD-Haut droit'), ('BG', 'BG-Bas gauche'), ('BD', 'BD-Bas droit')], default='HG', help_text='Position du multi-puits de calibration par défaut', max_length=8, verbose_name='Multi-puits de calibration par défaut')), ('calibration_default_feed', models.PositiveIntegerField(default=1000, help_text='Vitesse de déplacement pour la calibration en mm/mn', verbose_name='Vitesse de calibration')), ('calibration_default_step', models.FloatField(default=1.0, help_text='Pas de déplacement pour la calibration en mm', verbose_name='Pas de calibration')), ('calibration_default_duration', models.FloatField(default=3.0, help_text='Durée de pose entre chaque puits en s', verbose_name='Duruée calibration')), ('tracking', models.BooleanField(default=False, help_text='Suivi et analyse des planaires', verbose_name='Suivi')), ('tracking_setting', models.BooleanField(default=False, help_text='Autorise le réglage des valeurs par défaut dans la calibration', verbose_name='Réglage dans calibration')), ('tube_axis', models.CharField(choices=[('vertical', 'Vertical'), ('horizontal', 'Horizontal')], default='vertical', help_text='Axe du tube', max_length=16, null=True, verbose_name='Axe du puit')), ('min_area_px', models.PositiveIntegerField(default=20, help_text="surface minimale d'un contour pour être considéré valide (px²)", verbose_name='Surface minimale')), ('max_area_ratio', models.FloatField(default=0.1, help_text="surface maximale d'un contour en fraction de la frame (défaut 10%)", verbose_name='surface maximale ')), ('max_planarians', models.PositiveIntegerField(default=1, help_text='nombre maximum de planaires à suivre simultanément (1-10)', verbose_name='Max planaire')), ('merge_kernel_size', models.PositiveIntegerField(default=15, help_text='taille du kernel elliptique de fusion des fragments (px). Augmenter si fragments résiduels', verbose_name='Taille du kernel')), ('min_contour_dist_px', models.PositiveIntegerField(default=40, help_text='Distance min entre deux contours pour les considérer comme individus distincts. Défaut : 40px. Augmenter si IDs multiples persistent', verbose_name='Distance ')), ('active', models.BooleanField(default=False, verbose_name='Actif')), ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Auteur')), ], options={ 'verbose_name': 'Configuration', 'verbose_name_plural': 'Configuration', 'ordering': ['id'], }, ), migrations.CreateModel( name='MultiWell', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('label', models.CharField(blank=True, help_text='Label du multi-puit', max_length=100, null=True, verbose_name='Label')), ('position', models.CharField(choices=[('HG', 'HG-Haut gauche'), ('HD', 'HD-Haut droit'), ('BG', 'BG-Bas gauche'), ('BD', 'BD-Bas droit')], help_text='Position du multi-puits sur la table', max_length=8, null=True, unique=True, verbose_name='Position')), ('default', models.BooleanField(default=False, help_text='Multi-puit par défaut', verbose_name='Par défaut')), ('cols', models.PositiveSmallIntegerField(default=6, help_text='Nombre de colonnes', verbose_name='Colonnes')), ('rows', models.PositiveSmallIntegerField(default=4, help_text='Nombre de lignes', verbose_name='Lignes')), ('diameter', models.FloatField(default=16.0, help_text='Diamètre des tubes en mm', verbose_name='Diamètre')), ('row_def', models.CharField(default='A,B,C,D', help_text='Définition des lignes', max_length=16, null=True, verbose_name='Définition')), ('row_order', models.CharField(default='D,C,B,A', help_text='Ordre ligne de puit. Lecture en serpentin dans le sens des +- X', max_length=16, null=True, verbose_name='Ordre ligne')), ('crop_radius', models.PositiveSmallIntegerField(default=500, help_text='Rayon en pixels pour recadrer les images en px', verbose_name='Rayon de découpe recadrage')), ('order', models.PositiveSmallIntegerField(default=0, help_text='Ordre de lecture du multi-puit', verbose_name='Ordre')), ('duration', models.PositiveIntegerField(default=10, help_text='Durée de capture en secondes pour la calibration', verbose_name='Durée')), ('xbase', models.FloatField(default=50.0, help_text='Base origine X en mm', verbose_name='Origine X')), ('ybase', models.FloatField(default=50.0, help_text='Base origine Y en mm', verbose_name='Origine Y')), ('dx', models.FloatField(default=19.5, help_text='Pas ou interval sur X en mm', verbose_name='Pas X')), ('dy', models.FloatField(default=19.5, help_text='Pas ou interval sur Y en mm', verbose_name='Pas Y')), ('feed', models.PositiveIntegerField(default=1000, help_text='Vitesse déplacement en mm/mn ', verbose_name='Vitesse')), ('well_position', models.BooleanField(default=False, help_text='Positions des puits générées ?. Non => efface WellPosition et recalcule les positions', verbose_name='Positions')), ('active', models.BooleanField(default=True, verbose_name='Active')), ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Auteur')), ], options={ 'verbose_name': 'Multi-puits', 'verbose_name_plural': 'Multi-puits', 'ordering': ['order'], }, ), migrations.CreateModel( name='Experiment', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=100, null=True, verbose_name="Titre de l'expérience")), ('comment', models.TextField(blank=True, help_text="Descriptions de l'expérience", null=True, verbose_name='Commentaires')), ('identifier', models.CharField(max_length=100, null=True, unique=True, verbose_name="Identifiant d'expérience")), ('duration', models.PositiveIntegerField(default=120, help_text='Durée de la prise de vue en secondes', verbose_name='Durée')), ('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de création')), ('started', models.DateTimeField(blank=True, null=True, verbose_name='Date de début')), ('finished', models.DateTimeField(blank=True, null=True, verbose_name='Date de fin')), ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Auteur')), ('multiwell', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='scanner.multiwell', verbose_name='Multi-puits')), ], options={ 'verbose_name': 'Expérience', 'verbose_name_plural': 'Expériences', 'ordering': ['-created'], }, ), migrations.CreateModel( name='Session', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(help_text="Session d'expérience. 4 Multi-puits maximum", max_length=100, null=True, verbose_name='Nom de la session')), ('active', models.BooleanField(default=True, verbose_name='Active')), ('expected_export', models.DateTimeField(blank=True, help_text="Date d'exportation prévue", null=True, verbose_name='Exportation auto')), ('expected_scanning', models.DateTimeField(blank=True, help_text='Date du balayage prévue', null=True, verbose_name='Bbalayage auto')), ('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date de création')), ('finished', models.DateTimeField(blank=True, null=True, verbose_name='Date de fin')), ('export_status', models.CharField(choices=[('pending', 'En attente'), ('running', 'En cours'), ('done', 'Terminé'), ('error', 'Erreur')], default='pending', max_length=16, verbose_name='Status exportation')), ('export_exported_at', models.DateTimeField(blank=True, null=True, verbose_name='Exportation terminée à')), ('scanning_status', models.CharField(choices=[('pending', 'En attente'), ('running', 'En cours'), ('done', 'Terminé'), ('error', 'Erreur')], default='pending', max_length=16, verbose_name='Status scanning')), ('scanning_finished_at', models.DateTimeField(blank=True, null=True, verbose_name='Balayage terminé à')), ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Auteur')), ('export_task', models.OneToOneField(blank=True, help_text="Programmation de l'exportation des vidéos et images", null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='export_session', to='django_celery_beat.periodictask', verbose_name='Export médias')), ('scanning_task', models.OneToOneField(blank=True, help_text='Programmation du lancement du balayage', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='scanning_session', to='django_celery_beat.periodictask', verbose_name='Lancer le balayage')), ], options={ 'verbose_name': 'Session', 'verbose_name_plural': 'Sessions', 'ordering': ['-created'], }, ), migrations.CreateModel( name='Well', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(blank=True, help_text='Nom du puit: Ai..Di', max_length=4, null=True, unique=True, verbose_name='Nom')), ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Auteur')), ], options={ 'verbose_name': 'Puit', 'verbose_name_plural': 'Puits', 'ordering': ['name'], }, ), migrations.CreateModel( name='SessionExperiment', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Auteur')), ('experiment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='session_experiments', to='scanner.experiment', verbose_name='Expérience')), ('session', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='scanner.session', verbose_name='Session')), ], options={ 'verbose_name': "Expérience d'une session", 'verbose_name_plural': "Expériences d'une session", 'ordering': ['session'], 'unique_together': {('session', 'experiment')}, }, ), migrations.CreateModel( name='ExperimentWell', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('active', models.BooleanField(default=True, verbose_name='Active')), ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Auteur')), ('experiment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='experimentwell', to='scanner.experiment', verbose_name='Expérience')), ('well', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wellexperiment', to='scanner.well', verbose_name='Puit')), ], options={ 'verbose_name': 'Expérience puit', 'verbose_name_plural': 'Expériences puits', 'ordering': ['experiment', 'well'], 'unique_together': {('experiment', 'well')}, }, ), migrations.CreateModel( name='WellPosition', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('order', models.PositiveSmallIntegerField(default=0, help_text='Ordre de lecture du puit', verbose_name='Ordre')), ('x', models.FloatField(default=10.0, help_text='Axe X en mm', verbose_name='X')), ('y', models.FloatField(default=10.0, help_text='Axe Y en mm', verbose_name='Y')), ('px_per_mm', models.FloatField(default=50.0, help_text='Facteur de calibration optique', verbose_name='Pixels par mm')), ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Auteur')), ('multiwell', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='scanner.multiwell', verbose_name='Multi-puits')), ('well', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='scanner.well', verbose_name='Puit')), ], options={ 'verbose_name': 'Position du puit', 'verbose_name_plural': 'Position des puits', 'ordering': ['order'], 'unique_together': {('multiwell', 'well')}, }, ), ]