From 3aaa88a1aa51087e54665ad3e3e437e19f63e619 Mon Sep 17 00:00:00 2001 From: denis defolie Date: Tue, 5 May 2026 19:38:41 +0200 Subject: [PATCH] simulation --- test_tube_scanner/home/settings.py | 12 +- test_tube_scanner/make_videos.sh | 138 +++++++++++------- .../templates/planarian/experiment_form.html | 2 +- test_tube_scanner/planarian_sim.py | 2 + test_tube_scanner/scanner/admin.py | 4 +- test_tube_scanner/scanner/models.py | 13 +- test_tube_scanner/scanner/process.py | 2 +- 7 files changed, 110 insertions(+), 63 deletions(-) diff --git a/test_tube_scanner/home/settings.py b/test_tube_scanner/home/settings.py index 25c4ed4..b209a9b 100644 --- a/test_tube_scanner/home/settings.py +++ b/test_tube_scanner/home/settings.py @@ -21,8 +21,6 @@ print("Django BASE_DIR:", BASE_DIR) PACKAGE_DIR = BASE_DIR.parent APP_DATAS = PACKAGE_DIR / config('APP_DATAS') - - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ @@ -394,17 +392,15 @@ EXPORT_REMOTE_PATH = config("EXPORT_REMOTE_PATH") EXPORT_DESTINATIONS = ["local", "remote"] #EXPORT_DESTINATIONS = ["remote"] # only remote - +## tracker default parameters +# TRACKER_TUBE_AXIS = "vertical" -TRACKER_MIN_AREA = 20 # surface min planaire px x px +TRACKER_MIN_AREA = 20 # surface min planaire px x px TRACKER_MAX_AREA_RATIO = 0.1 # 5% de la frame = surface max acceptable -TRACKER_MAX_PLANARIANS = 1 +TRACKER_MAX_PLANARIANS = 4 TRACKER_MERGE_KERNEL_SIZE = 15 # augmenter si fragments résiduels TRACKER_MIN_CONTOUR_DIST_PX = 40 # augmenter si IDs multiples persistent - CALIBRATION_AUTO_DURATION = 45.0 CALIBRATION_AUTO_TIMEOUT = 2.5 - - diff --git a/test_tube_scanner/make_videos.sh b/test_tube_scanner/make_videos.sh index 6205a29..7e9bc6c 100755 --- a/test_tube_scanner/make_videos.sh +++ b/test_tube_scanner/make_videos.sh @@ -3,62 +3,100 @@ # Génère 24 vidéos pour simuler le balayage d'un multi-puit de 6x24 # A1..A6, B1..B6, C1..C6, D1..D6 # - +MODE="$1" PATH="./media/simulation" default_width=1000 # px default_height=1000 # px default_diameter=16.0 # mm -declare -A arguments=( - # key count length width fps duration seed bg-color arena-color arena-border shadow-color body-color body-dark body-light head-color thresh-immobile thresh-mobile thigmotaxis photo-mode photo-strength photo-x photo-y photo-sine-freq photo-radius chemo-strength chemo-x chemo-y chemo-radius avoid-strength avoid-radius aggreg-strength aggreg-radius chem-repulsion chem-decay - ["A1"]="4 0.40 0.30 15 600 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95" -) + #fps duration bg-color arena-color arena-border shadow-color body-color body-dark body-light head-color thresh-immobile thresh-mobile +BASE="15 60 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5" -declare -A arguments_all=( - # key count length width fps duration seed bg-color arena-color arena-border shadow-color body-color body-dark body-light head-color thresh-immobile thresh-mobile thigmotaxis photo-mode photo-strength photo-x photo-y photo-sine-freq photo-radius chemo-strength chemo-x chemo-y chemo-radius avoid-strength avoid-radius aggreg-strength aggreg-radius chem-repulsion chem-decay - ["A1"]="3 0.40 0.30 15 60 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95" - ["A2"]="1 0.42 0.32 15 60 96 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 fixed 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["A3"]="1 0.50 0.40 15 60 128 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 radial 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["A4"]="3 0.60 0.35 15 60 192 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.5 0.95" - ["A5"]="1 0.30 0.32 15 60 250 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["A6"]="2 0.55 0.38 15 60 32 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 1.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["B1"]="3 0.40 0.30 15 60 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95" - ["B2"]="1 0.42 0.32 15 60 96 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 fixed 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["B3"]="1 0.50 0.40 15 60 128 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 radial 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["B4"]="3 0.60 0.35 15 60 192 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.5 0.95" - ["B5"]="1 0.30 0.32 15 60 250 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["B6"]="2 0.55 0.38 15 60 32 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 1.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["C1"]="3 0.40 0.30 15 60 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95" - ["C2"]="1 0.42 0.32 15 60 96 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 fixed 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["C3"]="1 0.50 0.40 15 60 128 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 radial 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["C4"]="3 0.60 0.35 15 60 192 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.5 0.95" - ["C5"]="1 0.30 0.32 15 60 250 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["C6"]="2 0.55 0.38 15 60 32 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 1.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["D1"]="3 0.40 0.30 15 60 64 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.45 none 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 1.0 3.0 0.0 6.0 0.0 0.95" - ["D2"]="1 0.42 0.32 15 60 96 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 fixed 0.50 0.50 0.50 0.10 0.30 0.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["D3"]="1 0.50 0.40 15 60 128 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 radial 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["D4"]="3 0.60 0.35 15 60 192 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.5 0.95" - ["D5"]="1 0.30 0.32 15 60 250 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 0.5 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" - ["D6"]="2 0.55 0.38 15 60 32 #EBEBEB #FAFAFA #8C8C8C #C8C8C8 #A5A5A5 #373737 #D2D2D2 #828282 0.2 1.5 0.70 none 0.50 0.50 0.50 0.10 0.30 1.0 0.70 0.70 2.0 0.0 3.0 0.0 6.0 0.0 0.95" + #photo-mode photo-strength photo-x photo-y photo-sine-freq photo-radius +PHOTO_0="none 0.50 0.50 0.50 0.10 0.30" +PHOTO_1="fixed 0.50 0.50 0.50 0.10 0.40" +PHOTO_2="sine 0.50 0.50 0.50 0.10 0.50" +PHOTO_3="radial 0.50 0.50 0.50 0.10 0.60" -) + #chemo-strength chemo-x chemo-y chemo-radius +CHEMO_0="0.0 0.70 0.70 2.0" +CHEMO_1="0.5 0.70 0.70 2.0" +CHEMO_2="1.0 0.70 0.70 2.0" + + #avoid-strength avoid-radius aggreg-strength aggreg-radius chem-repulsion chem-decay +AVOID_0="0.0 3.0 0.0 6.0 0.0 0.95" +AVOID_1="0.5 3.0 0.0 6.0 0.5 0.75" +AVOID_2="1.0 3.0 0.0 6.0 1.0 0.65" + + #length width +SIZE_0="0.40 0.30" +SIZE_1="0.45 0.35" +SIZE_2="0.50 0.40" +SIZE_3="0.55 0.45" +SIZE_4="0.65 0.45" +SIZE_5="0.7 0.25" + +declare -A DEFAULT +declare -A ALL + +#=========================== count size seed base thigmotaxis photo chemo avoid +DEFAULT[default_simulation]="4 $SIZE_0 32 $BASE 0.45 $PHOTO_0 $CHEMO_0 $AVOID_1" -for key in "${!arguments[@]}"; do - args="${arguments[$key]}" - read -r count length width fps duration seed bg_color arena_color arena_border shadow_color \ - body_color body_dark body_light head_color thresh_immobile thresh_mobile thigmotaxis \ - photo_mode photo_strength photo_x photo_y photo_sine_freq photo_radius chemo_strength chemo_x chemo_y chemo_radius \ - avoid_strength avoid_radius aggreg_strength aggreg_radius chem_repulsion chem_decay <<< "$args" - - echo "==== Exécution de $PATH/$key.mp4" - ./planarian_sim.py --output "$PATH/$key.mp4" --default_width "$default_width" --default_height "$default_height" --default_diameter "$default_diameter" --no-csv \ - --count "$count" --length "$length" --width "$width" --duration "$duration" --fps "$fps" --seed "$seed" \ - --bg-color "$bg_color" --arena-color "$arena_color" --arena-border "$arena_border" --shadow-color "$shadow_color" \ - --body-color "$body_color" --body-dark "$body_dark" --body-light "$body_light" --head-color "$head_color" \ - --thresh-immobile "$thresh_immobile" --thresh-mobile "$thresh_mobile" --thigmotaxis "$thigmotaxis" \ - --photo-mode "$photo_mode" --photo-strength "$photo_strength" --photo-x "$photo_x" --photo-y "$photo_y" --photo-sine-freq "$photo_sine_freq" --photo-radius "$photo_radius" \ - --chemo-strength "$chemo_strength" --chemo-x "$chemo_x" --chemo-y "$chemo_y" --chemo-radius "$chemo_radius" \ - --avoid-strength "$avoid_strength" --avoid-radius "$avoid_radius" --aggreg-strength "$aggreg_strength" --aggreg-radius "$aggreg_radius" \ - --chem-repulsion "$chem_repulsion" --chem-decay "$chem_decay" -done +#======= count size seed base thigmotaxis photo chemo avoid +ALL[A1]="4 $SIZE_0 32 $BASE 0.45 $PHOTO_0 $CHEMO_0 $AVOID_0" +ALL[A2]="4 $SIZE_1 64 $BASE 0.50 $PHOTO_1 $CHEMO_1 $AVOID_1" +ALL[A3]="4 $SIZE_2 96 $BASE 0.55 $PHOTO_1 $CHEMO_2 $AVOID_2" +ALL[A4]="4 $SIZE_3 128 $BASE 0.60 $PHOTO_2 $CHEMO_0 $AVOID_1" +ALL[A5]="4 $SIZE_4 192 $BASE 0.65 $PHOTO_2 $CHEMO_1 $AVOID_0" +ALL[A6]="4 $SIZE_5 240 $BASE 0.75 $PHOTO_3 $CHEMO_2 $AVOID_2" +ALL[B1]="4 $SIZE_0 32 $BASE 0.45 $PHOTO_3 $CHEMO_0 $AVOID_0" +ALL[B2]="4 $SIZE_1 64 $BASE 0.50 $PHOTO_0 $CHEMO_1 $AVOID_1" +ALL[B3]="4 $SIZE_2 96 $BASE 0.55 $PHOTO_0 $CHEMO_2 $AVOID_0" +ALL[B4]="4 $SIZE_3 128 $BASE 0.65 $PHOTO_0 $CHEMO_1 $AVOID_2" +ALL[B5]="4 $SIZE_4 192 $BASE 0.85 $PHOTO_0 $CHEMO_2 $AVOID_0" +ALL[B6]="4 $SIZE_5 240 $BASE 0.95 $PHOTO_0 $CHEMO_0 $AVOID_0" +ALL[C1]="4 $SIZE_0 32 $BASE 0.40 $PHOTO_0 $CHEMO_0 $AVOID_1" +ALL[C2]="4 $SIZE_1 64 $BASE 0.30 $PHOTO_0 $CHEMO_0 $AVOID_0" +ALL[C3]="4 $SIZE_2 96 $BASE 0.55 $PHOTO_0 $CHEMO_1 $AVOID_0" +ALL[C4]="4 $SIZE_3 128 $BASE 0.45 $PHOTO_0 $CHEMO_2 $AVOID_2" +ALL[C5]="4 $SIZE_4 192 $BASE 0.50 $PHOTO_0 $CHEMO_0 $AVOID_0" +ALL[C6]="4 $SIZE_5 240 $BASE 0.65 $PHOTO_0 $CHEMO_1 $AVOID_0" +ALL[D1]="4 $SIZE_0 32 $BASE 0.70 $PHOTO_0 $CHEMO_2 $AVOID_1" +ALL[D2]="4 $SIZE_1 64 $BASE 0.65 $PHOTO_0 $CHEMO_0 $AVOID_0" +ALL[D3]="4 $SIZE_2 96 $BASE 0.75 $PHOTO_0 $CHEMO_1 $AVOID_2" +ALL[D4]="4 $SIZE_3 128 $BASE 0.85 $PHOTO_0 $CHEMO_0 $AVOID_0" +ALL[D5]="4 $SIZE_4 192 $BASE 0.65 $PHOTO_0 $CHEMO_2 $AVOID_0" +ALL[D6]="4 $SIZE_5 240 $BASE 0.45 $PHOTO_0 $CHEMO_0 $AVOID_0" + + +export_video() { + local -n arguments=$1 + + for key in "${!arguments[@]}"; do + args="${arguments[$key]}" + read -r count length width seed fps duration bg_color arena_color arena_border shadow_color \ + body_color body_dark body_light head_color thresh_immobile thresh_mobile thigmotaxis \ + photo_mode photo_strength photo_x photo_y photo_sine_freq photo_radius chemo_strength chemo_x chemo_y chemo_radius \ + avoid_strength avoid_radius aggreg_strength aggreg_radius chem_repulsion chem_decay <<< "$args" + + echo "==== Exécution de $PATH/$key.mp4" + + ./planarian_sim.py --output "$PATH/$key.mp4" --default_width "$default_width" --default_height "$default_height" --default_diameter "$default_diameter" --no-csv \ + --count "$count" --length "$length" --width "$width" --duration "$duration" --fps "$fps" --seed "$seed" \ + --bg-color "$bg_color" --arena-color "$arena_color" --arena-border "$arena_border" --shadow-color "$shadow_color" \ + --body-color "$body_color" --body-dark "$body_dark" --body-light "$body_light" --head-color "$head_color" \ + --thresh-immobile "$thresh_immobile" --thresh-mobile "$thresh_mobile" --thigmotaxis "$thigmotaxis" \ + --photo-mode "$photo_mode" --photo-strength "$photo_strength" --photo-x "$photo_x" --photo-y "$photo_y" --photo-sine-freq "$photo_sine_freq" --photo-radius "$photo_radius" \ + --chemo-strength "$chemo_strength" --chemo-x "$chemo_x" --chemo-y "$chemo_y" --chemo-radius "$chemo_radius" \ + --avoid-strength "$avoid_strength" --avoid-radius "$avoid_radius" --aggreg-strength "$aggreg_strength" --aggreg-radius "$aggreg_radius" \ + --chem-repulsion "$chem_repulsion" --chem-decay "$chem_decay" + done +} + +if [ "$MODE" = "all" ]; then + export_video ALL +else + export_video DEFAULT +fi + diff --git a/test_tube_scanner/planarian/templates/planarian/experiment_form.html b/test_tube_scanner/planarian/templates/planarian/experiment_form.html index be2c48a..492d734 100644 --- a/test_tube_scanner/planarian/templates/planarian/experiment_form.html +++ b/test_tube_scanner/planarian/templates/planarian/experiment_form.html @@ -407,7 +407,7 @@
- +

{% trans "Rayon de répulsion courte portée (mm)" %}

diff --git a/test_tube_scanner/planarian_sim.py b/test_tube_scanner/planarian_sim.py index c6ff9a0..9f8277e 100755 --- a/test_tube_scanner/planarian_sim.py +++ b/test_tube_scanner/planarian_sim.py @@ -174,6 +174,7 @@ def parse_args(): help="Position Y de la nourriture (fraction 0-1)") cg.add_argument("--chemo-radius", type=float, default=2.0, help="Rayon d'influence du chimiotactisme en mm") + # --- Interactions inter-individus --- ig = parser.add_argument_group( @@ -186,6 +187,7 @@ def parse_args(): help="Rayon d'évitement en mm") ig.add_argument("--aggreg-strength", type=float, default=0.0, help="Force d'agrégation — attraction vers les congénères (0=désactivé)") + ig.add_argument("--aggreg-radius", type=float, default=6.0, help="Rayon d'agrégation en mm (doit être > --avoid-radius)") ig.add_argument("--chem-repulsion", type=float, default=0.0, diff --git a/test_tube_scanner/scanner/admin.py b/test_tube_scanner/scanner/admin.py index 9b0f6f6..d2336b1 100644 --- a/test_tube_scanner/scanner/admin.py +++ b/test_tube_scanner/scanner/admin.py @@ -73,8 +73,8 @@ class WellPositionAdmin(admin.ModelAdmin): class ExperimentAdmin(admin.ModelAdmin): #inlines = (ExperimenConfigInline,) list_filter = ('session_experiments__session', 'author', ) - list_display = ('title', 'author', 'multiwell', 'created', 'started', 'finished') - readonly_fields = ('created', 'started', 'finished', ) + list_display = ('title', 'author', 'identifier', 'multiwell', 'created', 'started', 'finished') + readonly_fields = ('created', 'identifier', 'started', 'finished', ) class SessionExperimentInlineAdmin(admin.TabularInline): diff --git a/test_tube_scanner/scanner/models.py b/test_tube_scanner/scanner/models.py index b4958dc..c37d1bb 100644 --- a/test_tube_scanner/scanner/models.py +++ b/test_tube_scanner/scanner/models.py @@ -7,7 +7,7 @@ import json from django_celery_beat.models import PeriodicTask, ClockedSchedule from django.dispatch import receiver from django.db.models.signals import post_save, post_delete - +from django.utils.text import slugify from django.utils import timezone from django.db import models from django.contrib.auth.models import User @@ -227,12 +227,23 @@ def create_well_position(sender, instance, created, **kwargs): class Experiment(models.Model): title = models.CharField(_("Titre de l'expérience"), max_length=100, null=True, blank=False) comment = models.TextField(_("Commentaires"), help_text=_("Descriptions de l'expérience"), null=True, blank=True) + 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) 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}') + super().save(*args, **kwargs) + + + @classmethod + def by_identifier(cls, identifier): + return Experiment.objects.filter(identifier__exact=identifier).first() + class Meta: ordering = ['-created', ] diff --git a/test_tube_scanner/scanner/process.py b/test_tube_scanner/scanner/process.py index da29ab6..5463b79 100644 --- a/test_tube_scanner/scanner/process.py +++ b/test_tube_scanner/scanner/process.py @@ -185,7 +185,7 @@ class ScannerProcess(Task): if capture_type == 'file': from modules.videofile_capture import VideoFileCapture self.cam = VideoFileCapture( - video_file=settings.MEDIA_ROOT / 'simulation' / 'default-simulation.mp4', + video_file=settings.MEDIA_ROOT / 'simulation' / 'default_simulation.mp4', fps=self.video_fps, width=self.video_width, height=self.video_height,