From 500950017fe147aa53ba8f956d1aaf5c4edc84b9 Mon Sep 17 00:00:00 2001 From: denis defolie Date: Sat, 9 May 2026 23:02:11 +0200 Subject: [PATCH] planarian --- test_tube_scanner/planarian/admin.py | 4 +-- test_tube_scanner/planarian/models.py | 40 ++++++------------------ test_tube_scanner/scanner/models.py | 45 ++++++++++++++++++++------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/test_tube_scanner/planarian/admin.py b/test_tube_scanner/planarian/admin.py index f0470d9..cc56ccd 100644 --- a/test_tube_scanner/planarian/admin.py +++ b/test_tube_scanner/planarian/admin.py @@ -9,7 +9,7 @@ from .models import ExperimentConfig class ExperimentConfigAdmin(admin.ModelAdmin): """Admin Django pour les configurations d'expérience.""" #readonly_fields = ('experiment', ) - readonly_fields = ("identifier", 'px_per_mm', 'fps', 'well_radius_mm',) + readonly_fields = ('px_per_mm', 'fps', 'well_radius_mm',) list_display = ("experiment", "well", "px_per_mm", "fps", "thresh_immobile", "thresh_mobile", "photo_mode", "chemo_strength", "created_at") @@ -19,7 +19,7 @@ class ExperimentConfigAdmin(admin.ModelAdmin): fieldsets = ( (_("Identification"), { - "fields": ("identifier", "experiment", "well", "description"), + "fields": ("experiment", "well", "description"), }), (_("Calibration optique: générée lors de la calibration"), { "fields": ("px_per_mm", "fps", "well_radius_mm"), diff --git a/test_tube_scanner/planarian/models.py b/test_tube_scanner/planarian/models.py index b7c0e6c..765ba7a 100644 --- a/test_tube_scanner/planarian/models.py +++ b/test_tube_scanner/planarian/models.py @@ -1,27 +1,22 @@ # planarian/models.py from django.db import models -from django.dispatch import receiver -from django.db.models.signals import post_save #from django.conf import settings - from django.utils.translation import gettext_lazy as _ from django.contrib.auth.models import User -from scanner.models import Experiment, Well, WellPosition -from scanner.constants import ScannerConstants +from scanner.models import Experiment, Well + class ExperimentConfig(models.Model): """ Paramètres d'une expérience PlanarianScanner. Peut être créé depuis Django admin, une vue formulaire ou un import CSV. """ - author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Auteur", null=True, blank=True) - - # --- Identification --- - identifier = models.CharField( max_length=100, verbose_name=_("Identifiant d'expérience"), help_text=_("session_1-HD-2026-04-27"), null=True, blank=True ) + author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Auteur", null=True, blank=True) experiment = models.ForeignKey(Experiment, verbose_name="Expérience", on_delete=models.CASCADE, related_name="experiment_well", null=True, blank=False) well = models.ForeignKey(Well, verbose_name="Puit", on_delete=models.CASCADE, related_name="well_experiment", null=True, blank=False ) - description = models.TextField( blank=True, verbose_name=_("Description"), ) + + description = models.TextField( blank=True, verbose_name=_("Description"), default="-") created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Créé le")) # --- Calibration optique --- @@ -121,8 +116,8 @@ class ExperimentConfig(models.Model): aggreg_radius_mm = models.FloatField(default=6.0, verbose_name=_("Rayon agrégation (mm)")) class Meta: - verbose_name = _("Configuration expérience") - verbose_name_plural = _("Configuration des expériences") + verbose_name = _("Configuration d'une expérience") + verbose_name_plural = _("Configurations des expériences") unique_together = ("experiment", "well") ordering = ["-created_at"] @@ -135,8 +130,9 @@ class ExperimentConfig(models.Model): def to_params_dict(self) -> dict: """Retourne un dict compatible avec ExperimentParams.""" return { - "experiment": self.identifier, + "experiment": self.experiment.identifier, "well": self.well.name, + "px_per_mm": self.px_per_mm, "fps": self.fps, "well_radius_mm": self.well_radius_mm, @@ -158,22 +154,4 @@ class ExperimentConfig(models.Model): "avoid_radius_mm": self.avoid_radius_mm, "aggreg_radius_mm": self.aggreg_radius_mm, } - - def save(self, *args, **kwargs): - session = self.get_session() - dte = self.experiment.created.isoformat()[:19] - self.identifier = f'{dte}-{session.id}-{self.experiment.id}-{self.experiment.multiwell.position}-{self.well.name}' - super().save(*args, **kwargs) - - -@receiver(post_save, sender=ExperimentConfig) -def create_well_position(sender, instance, created, **kwargs): - if created: - active_well = WellPosition.active_well(instance.experiment.multiwell, instance.well) - instance.px_per_mm = active_well.px_per_mm - instance.well_radius_mm = instance.experiment.multiwell.diameter / 2 - conf = ScannerConstants().get() - instance.fps = conf.video_frame_rate - instance.save() - diff --git a/test_tube_scanner/scanner/models.py b/test_tube_scanner/scanner/models.py index fdb77b4..5568d5a 100644 --- a/test_tube_scanner/scanner/models.py +++ b/test_tube_scanner/scanner/models.py @@ -188,12 +188,16 @@ class WellPosition(models.Model): @classmethod def active_well(cls, multiwell, well): - return WellPosition.objects.filter(multiwell_id=multiwell.id, well_id=well.id).first() + return WellPosition.objects.filter(multiwell_id=multiwell.id, well_id=well.id).first() + + @classmethod + def well_by_multiwell(cls, multiwell): + return WellPosition.objects.filter(multiwell_id=multiwell.id).all() class Meta: ordering = ['order'] unique_together = ["multiwell", "well"] - verbose_name = _("Position d'un puit") + verbose_name = _("Position du puit") verbose_name_plural = _("Position des puits") def __str__(self): @@ -237,15 +241,16 @@ class Experiment(models.Model): identifier = models.CharField(_("Identifiant d'expérience"), unique=True, max_length=100, null=True, blank=False ) author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Auteur", null=True, blank=True) - multiwell = models.ForeignKey(MultiWell, verbose_name=_("Multi-puits"), on_delete=models.SET_NULL, null=True, blank=True) + multiwell = models.ForeignKey(MultiWell, verbose_name=_("Multi-puits"), on_delete=models.SET_NULL, null=True, blank=True) duration = models.PositiveIntegerField(_("Durée"), help_text=_('Durée de la prise de vue en secondes'), blank=False, default=120) created = models.DateTimeField(_("Date de création"), default=timezone.now) started = models.DateTimeField (_("Date de début"), null=True, blank=True) finished = models.DateTimeField (_("Date de fin"), null=True, blank=True) + def save(self, *args, **kwargs): - self.identifier = slugify(f'{self.title}') + self.identifier = slugify(f'{self.created.isoformat()[:19]}-{self.id}-{self.multiwell.position}') super().save(*args, **kwargs) @@ -399,7 +404,7 @@ class SessionExperiment(models.Model): verbose_name_plural = _("Sessions expériences") def __str__(self): - return f'{self.session.name}' + return f'{self.session.id}: {self.experiment.title}' class ExperimentWell(models.Model): @@ -408,23 +413,39 @@ class ExperimentWell(models.Model): well = models.ForeignKey(Well, verbose_name=_("Puit"), on_delete=models.SET_NULL, null=True, blank=True, related_name="wellexperiment") active = models.BooleanField(_("Active"), default=True) + @classmethod + def well_by_experiment(cls, experiment_id): + return ExperimentWell.objects.filter(experiment__id=experiment_id, active=True).order_by('well__name') + @classmethod def wellname_by_experiment(cls, experiment_id): - return [ ew.well.name for ew in ExperimentWell.objects.filter(experiment__id=experiment_id, active=True).order_by('well__name') ] + return [ ew.well.name for ew in ExperimentWell.objects.filter(experiment__id=experiment_id, active=True).order_by('well__name') ] class Meta: ordering = ['experiment', 'well'] unique_together = ["experiment", "well", ] verbose_name = _("Expérience puit") - verbose_name_plural = _("Expériences puitd") + verbose_name_plural = _("Expériences puits") def __str__(self): return f'{self.experiment.title}' - @receiver(post_save, sender=Experiment) def create_experiment_well(sender, instance, created, **kwargs): - wells = Well.objects.all() - for well in wells: - ExperimentWell.objects.get_or_create(experiment=instance, well=well, author=instance.author, defaults={'active':True}) - + from planarian.models import ExperimentConfig + from .constants import ScannerConstants + + wellposition = WellPosition.well_by_multiwell(instance.multiwell) + for wp in wellposition: + ExperimentWell.objects.get_or_create(experiment=instance, well=wp.well, author=instance.author, defaults={'active':True}) + + ExperimentConfig.objects.get_or_create( + experiment=instance, + well=wp.well, + author=instance.author, + defaults={ + 'px_per_mm': wp.px_per_mm, + 'fps': ScannerConstants().get().video_frame_rate, + 'well_radius_mm': instance.multiwell.diameter / 2, + } + )