planarian

This commit is contained in:
2026-05-09 23:02:11 +02:00
parent c7caccd951
commit 500950017f
3 changed files with 44 additions and 45 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ from .models import ExperimentConfig
class ExperimentConfigAdmin(admin.ModelAdmin): class ExperimentConfigAdmin(admin.ModelAdmin):
"""Admin Django pour les configurations d'expérience.""" """Admin Django pour les configurations d'expérience."""
#readonly_fields = ('experiment', ) #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", list_display = ("experiment", "well", "px_per_mm", "fps",
"thresh_immobile", "thresh_mobile", "thresh_immobile", "thresh_mobile",
"photo_mode", "chemo_strength", "created_at") "photo_mode", "chemo_strength", "created_at")
@@ -19,7 +19,7 @@ class ExperimentConfigAdmin(admin.ModelAdmin):
fieldsets = ( fieldsets = (
(_("Identification"), { (_("Identification"), {
"fields": ("identifier", "experiment", "well", "description"), "fields": ("experiment", "well", "description"),
}), }),
(_("Calibration optique: générée lors de la calibration"), { (_("Calibration optique: générée lors de la calibration"), {
"fields": ("px_per_mm", "fps", "well_radius_mm"), "fields": ("px_per_mm", "fps", "well_radius_mm"),
+9 -31
View File
@@ -1,27 +1,22 @@
# planarian/models.py # planarian/models.py
from django.db import models 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.conf import settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User from django.contrib.auth.models import User
from scanner.models import Experiment, Well, WellPosition from scanner.models import Experiment, Well
from scanner.constants import ScannerConstants
class ExperimentConfig(models.Model): class ExperimentConfig(models.Model):
""" """
Paramètres d'une expérience PlanarianScanner. Paramètres d'une expérience PlanarianScanner.
Peut être créé depuis Django admin, une vue formulaire ou un import CSV. 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) 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 )
experiment = models.ForeignKey(Experiment, verbose_name="Expérience", on_delete=models.CASCADE, related_name="experiment_well", null=True, blank=False) 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 ) 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")) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Créé le"))
# --- Calibration optique --- # --- Calibration optique ---
@@ -121,8 +116,8 @@ class ExperimentConfig(models.Model):
aggreg_radius_mm = models.FloatField(default=6.0, verbose_name=_("Rayon agrégation (mm)")) aggreg_radius_mm = models.FloatField(default=6.0, verbose_name=_("Rayon agrégation (mm)"))
class Meta: class Meta:
verbose_name = _("Configuration expérience") verbose_name = _("Configuration d'une expérience")
verbose_name_plural = _("Configuration des expériences") verbose_name_plural = _("Configurations des expériences")
unique_together = ("experiment", "well") unique_together = ("experiment", "well")
ordering = ["-created_at"] ordering = ["-created_at"]
@@ -135,8 +130,9 @@ class ExperimentConfig(models.Model):
def to_params_dict(self) -> dict: def to_params_dict(self) -> dict:
"""Retourne un dict compatible avec ExperimentParams.""" """Retourne un dict compatible avec ExperimentParams."""
return { return {
"experiment": self.identifier, "experiment": self.experiment.identifier,
"well": self.well.name, "well": self.well.name,
"px_per_mm": self.px_per_mm, "px_per_mm": self.px_per_mm,
"fps": self.fps, "fps": self.fps,
"well_radius_mm": self.well_radius_mm, "well_radius_mm": self.well_radius_mm,
@@ -158,22 +154,4 @@ class ExperimentConfig(models.Model):
"avoid_radius_mm": self.avoid_radius_mm, "avoid_radius_mm": self.avoid_radius_mm,
"aggreg_radius_mm": self.aggreg_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()
+33 -12
View File
@@ -188,12 +188,16 @@ class WellPosition(models.Model):
@classmethod @classmethod
def active_well(cls, multiwell, well): 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: class Meta:
ordering = ['order'] ordering = ['order']
unique_together = ["multiwell", "well"] unique_together = ["multiwell", "well"]
verbose_name = _("Position d'un puit") verbose_name = _("Position du puit")
verbose_name_plural = _("Position des puits") verbose_name_plural = _("Position des puits")
def __str__(self): 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 ) 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) 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) 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) created = models.DateTimeField(_("Date de création"), default=timezone.now)
started = models.DateTimeField (_("Date de début"), null=True, blank=True) started = models.DateTimeField (_("Date de début"), null=True, blank=True)
finished = models.DateTimeField (_("Date de fin"), null=True, blank=True) finished = models.DateTimeField (_("Date de fin"), null=True, blank=True)
def save(self, *args, **kwargs): 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) super().save(*args, **kwargs)
@@ -399,7 +404,7 @@ class SessionExperiment(models.Model):
verbose_name_plural = _("Sessions expériences") verbose_name_plural = _("Sessions expériences")
def __str__(self): def __str__(self):
return f'{self.session.name}' return f'{self.session.id}: {self.experiment.title}'
class ExperimentWell(models.Model): 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") 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) 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 @classmethod
def wellname_by_experiment(cls, experiment_id): 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: class Meta:
ordering = ['experiment', 'well'] ordering = ['experiment', 'well']
unique_together = ["experiment", "well", ] unique_together = ["experiment", "well", ]
verbose_name = _("Expérience puit") verbose_name = _("Expérience puit")
verbose_name_plural = _("Expériences puitd") verbose_name_plural = _("Expériences puits")
def __str__(self): def __str__(self):
return f'{self.experiment.title}' return f'{self.experiment.title}'
@receiver(post_save, sender=Experiment) @receiver(post_save, sender=Experiment)
def create_experiment_well(sender, instance, created, **kwargs): def create_experiment_well(sender, instance, created, **kwargs):
wells = Well.objects.all() from planarian.models import ExperimentConfig
for well in wells: from .constants import ScannerConstants
ExperimentWell.objects.get_or_create(experiment=instance, well=well, author=instance.author, defaults={'active':True})
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,
}
)