experiment
This commit is contained in:
@@ -10,9 +10,9 @@ 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 = ('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", "active", "px_per_mm", "fps",
|
||||||
"thresh_immobile", "thresh_mobile",
|
"thresh_immobile", "thresh_mobile",
|
||||||
"photo_mode", "chemo_strength", "created_at")
|
"photo_mode", "chemo_strength", "created_at", )
|
||||||
list_filter = ("experiment", "photo_mode", "tube_axis")
|
list_filter = ("experiment", "photo_mode", "tube_axis")
|
||||||
search_fields = ("experiment", "well", "description")
|
search_fields = ("experiment", "well", "description")
|
||||||
ordering = ("-created_at",)
|
ordering = ("-created_at",)
|
||||||
|
|||||||
@@ -5,15 +5,17 @@ import io
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from scanner import models
|
||||||
from .models import ExperimentConfig
|
from .models import ExperimentConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExperimentConfigForm(forms.ModelForm):
|
class ExperimentConfigForm(forms.ModelForm):
|
||||||
"""Formulaire de saisie/modification d'un ExperimentConfig."""
|
"""Formulaire de saisie/modification d'un ExperimentConfig."""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['identifier'].disabled = True
|
#self.fields['identifier'].disabled = True
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExperimentConfig
|
model = ExperimentConfig
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ class ExperimentConfig(models.Model):
|
|||||||
description = models.TextField( blank=True, verbose_name=_("Description"), default="-")
|
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"))
|
||||||
|
|
||||||
|
active = models.BooleanField(_("Active"), default=True)
|
||||||
|
|
||||||
# --- Calibration optique ---
|
# --- Calibration optique ---
|
||||||
# px_per_mm, fps, well_radius_mm
|
# px_per_mm, fps, well_radius_mm
|
||||||
px_per_mm = models.FloatField(
|
px_per_mm = models.FloatField(
|
||||||
@@ -122,7 +124,7 @@ class ExperimentConfig(models.Model):
|
|||||||
ordering = ["-created_at"]
|
ordering = ["-created_at"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.experiment} / {self.well.name}"
|
return f"{self.experiment}:{self.well.name}"
|
||||||
|
|
||||||
def get_session(self):
|
def get_session(self):
|
||||||
return self.experiment.session_experiments.first() if self.experiment else None
|
return self.experiment.session_experiments.first() if self.experiment else None
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
.multiwell_cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.multiwell {
|
||||||
|
padding: 0.2em ;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
{% extends "scanner/base.html" %}
|
{% extends "scanner/base.html" %}
|
||||||
{% load i18n home_tags scanner_tags %}
|
{% load i18n home_tags scanner_tags %}
|
||||||
|
{% block styles %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link href="/static/planarian/css/planarian.css" rel="stylesheet">
|
||||||
|
{% endblock %}
|
||||||
{% block columns %}{% endblock %}
|
{% block columns %}{% endblock %}
|
||||||
|
|
||||||
{#% block sidebar_list %#}
|
{% block sidebar_list %}
|
||||||
<!--
|
|
||||||
<a href="/" class="w3-bar-item w3-btn w3-hover-opacity"><i class="fa-solid fa-house w3-text-orange w3-xlarge"></i> {% trans "Retour accueil" %}</a>
|
<a href="/" class="w3-bar-item w3-btn w3-hover-opacity"><i class="fa-solid fa-house w3-text-orange w3-xlarge"></i> {% trans "Retour accueil" %}</a>
|
||||||
<form method="post" class="w3-bar-item" action="">
|
<form method="post" class="w3-bar-item" action="">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<select id="_sid" name="_sid" class="w3-select w3-margin-bottom" onchange="this.form.submit()" title="{% trans "Ensemble d'expériences" %}">
|
<select id="_sid" name="_sid" class="w3-select w3-margin-bottom" onchange="this.form.submit()" title="{% trans "Ensemble d'expériences" %}">
|
||||||
<option value="0">---- {% trans "Session" %}</option>
|
<option value="0">---- {% trans "Session" %}</option>
|
||||||
{% for s in sessions %}
|
{% for s in sessions %}
|
||||||
<option value="{{ s.id }}" {% if s.id == current_session.id %}selected{% endif %}>{{ s.name }}</option>
|
<option value="{{ s.id }}" {% if s.id == current_session.id %}selected{% endif %}>{{ s }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<div class="w3-margin-left w3-margin-bottom">
|
<div class="w3-margin-left w3-margin-bottom">
|
||||||
@@ -20,17 +22,17 @@
|
|||||||
<label>{{ ss.multiwell }}</label>
|
<label>{{ ss.multiwell }}</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% if current_session.id and current_experiment %}
|
|
||||||
{% multiwell_cards current_session.id current_experiment %}
|
|
||||||
{% endif %}
|
|
||||||
</form>
|
</form>
|
||||||
{% if current_session.id %}
|
{% if current_session.id and current_experiment.id %}
|
||||||
<a href="{% url 'scanner:main' %}" class="w3-bar-item w3-btn w3-hover-opacity">
|
<hr class="w3-bar-item divider w3-grey">
|
||||||
<i class="fa-solid fa-film w3-text-green w3-xlarge""></i> {% trans "Balayage multi-puits" %}
|
<form method="post" class="w3-bar-item" action="{% url 'planarian:export-csv' %}">
|
||||||
</a>
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="_sid" value="{{ current_session.id }}">
|
||||||
|
<input type="hidden" name="_expid" value="{{ current_experiment.id }}">
|
||||||
|
<button type="submit" class="w3-button w3-blue w3-round w3-padding w3-margin-top">📥 {% trans "Exporter CSV" %}</button>
|
||||||
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
-->
|
{% endblock %}
|
||||||
{#% endblock %#}
|
|
||||||
|
|
||||||
{% block js_footer %}
|
{% block js_footer %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|||||||
@@ -33,6 +33,9 @@
|
|||||||
<form method="post" novalidate>
|
<form method="post" novalidate>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<input type="hidden" id="_sid" name="_sid" value="{{ current_session.id }}">
|
||||||
|
<input type="hidden" id="_expid" name="_expid" value="{{ current_experiment.id }}">
|
||||||
|
|
||||||
<!-- Erreurs globales du formulaire -->
|
<!-- Erreurs globales du formulaire -->
|
||||||
{% if form.non_field_errors %}
|
{% if form.non_field_errors %}
|
||||||
<div class="w3-panel w3-red w3-round">
|
<div class="w3-panel w3-red w3-round">
|
||||||
|
|||||||
@@ -309,5 +309,4 @@
|
|||||||
if (e.target === modal) modal.style.display = 'none';
|
if (e.target === modal) modal.style.display = 'none';
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "planarian/base.html" %}
|
{% extends "planarian/base.html" %}
|
||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -47,6 +48,8 @@
|
|||||||
<!-- Formulaire -->
|
<!-- Formulaire -->
|
||||||
<form method="post" novalidate>
|
<form method="post" novalidate>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<input type="hidden" id="_sid" name="_sid" value="{{ current_session.id }}">
|
||||||
|
<input type="hidden" id="_expid" name="_expid" value="{{ current_experiment.id }}">
|
||||||
|
|
||||||
{% if form.non_field_errors %}
|
{% if form.non_field_errors %}
|
||||||
<div class="w3-panel w3-red w3-round">
|
<div class="w3-panel w3-red w3-round">
|
||||||
@@ -70,32 +73,23 @@
|
|||||||
{% if form.experiment.help_text %}
|
{% if form.experiment.help_text %}
|
||||||
<p class="w3-small w3-text-grey" style="margin:0 0 4px;">{{ form.experiment.help_text }}</p>
|
<p class="w3-small w3-text-grey" style="margin:0 0 4px;">{{ form.experiment.help_text }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<!-- Saisie libre ou sélection depuis les configs existantes -->
|
<select id="experiment-list">
|
||||||
<input list="experiment-list" name="experiment" id="id_experiment"
|
{% for cfg in experiments %}
|
||||||
value="{{ form.experiment.value|default:'' }}"
|
<option value="{{ cfg.identifier}}" {% if current_experiment.identifier == cfg.identifier %}selected{% endif %} >{{ cfg.identifier }}</option>
|
||||||
class="w3-input w3-border w3-round"
|
|
||||||
placeholder="{% trans 'Ex : exp_2026_04_25' %}">
|
|
||||||
<datalist id="experiment-list">
|
|
||||||
{% for cfg in experiment_choices %}
|
|
||||||
<option value="{{ cfg }}">
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</datalist>
|
</select>
|
||||||
{% if form.experiment.errors %}
|
{% if form.experiment.errors %}
|
||||||
<span class="w3-text-red w3-small">{{ form.experiment.errors|join:", " }}</span>
|
<!--span class="w3-text-red w3-small">{{ form.experiment.errors|join:", " }}</span-->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w3-col m5 s12 w3-margin-bottom">
|
<div class="w3-col m5 s12 w3-margin-bottom">
|
||||||
<label class="w3-text-blue"><b>{{ form.well.label }}</b></label>
|
<label class="w3-text-blue"><b>{{ form.well.label }}</b></label>
|
||||||
<input list="well-list" name="well" id="id_well"
|
<select id="id_well">
|
||||||
value="{{ form.well.value|default:'' }}"
|
|
||||||
class="w3-input w3-border w3-round"
|
|
||||||
placeholder="{% trans 'Ex : A1' %}">
|
|
||||||
<datalist id="well-list">
|
|
||||||
{% for w in well_choices %}
|
{% for w in well_choices %}
|
||||||
<option value="{{ w }}">
|
<option value="{{ w.name }}">{{ w.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</datalist>
|
</select>
|
||||||
{% if form.well.errors %}
|
{% if form.well.errors %}
|
||||||
<span class="w3-text-red w3-small">{{ form.well.errors|join:", " }}</span>
|
<span class="w3-text-red w3-small">{{ form.well.errors|join:", " }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ from django.views.decorators.http import require_GET
|
|||||||
|
|
||||||
from .forms import CsvImportForm, ExperimentConfigForm, ExportCsvForm
|
from .forms import CsvImportForm, ExperimentConfigForm, ExportCsvForm
|
||||||
from .models import ExperimentConfig
|
from .models import ExperimentConfig
|
||||||
|
|
||||||
from modules.planarian_metrics import ExperimentParams, ReductStoreClient
|
from modules.planarian_metrics import ExperimentParams, ReductStoreClient
|
||||||
from modules.system_stats import get_cached_stats, start_background_updater
|
from modules.system_stats import get_cached_stats, start_background_updater
|
||||||
from scanner.constants import ScannerConstants
|
from scanner.constants import ScannerConstants
|
||||||
|
from scanner import models
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -37,21 +39,67 @@ def stats_view(request):
|
|||||||
return JsonResponse({"error": str(e)}, status=500)
|
return JsonResponse({"error": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_reduct_client() -> ReductStoreClient:
|
||||||
|
"""Instancie le client ReductStore depuis les settings Django."""
|
||||||
|
return ReductStoreClient(url=settings.REDUCTSTORE_URL, token=settings.REDUCTSTORE_TOKEN)
|
||||||
|
|
||||||
|
|
||||||
def global_context(request, **ctx):
|
def global_context(request, **ctx):
|
||||||
|
default_multiwell = models.MultiWell.objects.filter(default=True).first()
|
||||||
conf = ScannerConstants().get()
|
conf = ScannerConstants().get()
|
||||||
return dict(
|
return dict(
|
||||||
app_title=settings.APP_TITLE,
|
app_title=settings.APP_TITLE,
|
||||||
app_sub_title=settings.APP_SUB_TITLE,
|
app_sub_title=settings.APP_SUB_TITLE,
|
||||||
domain_server=settings.DOMAIN_SERVER,
|
domain_server=settings.DOMAIN_SERVER,
|
||||||
|
local_ip_server=settings.LOCAL_IP_SERVER,
|
||||||
|
host_port=settings.SERVER_HOST_PORT,
|
||||||
conf=conf,
|
conf=conf,
|
||||||
|
default_position = default_multiwell.position or 'HD',
|
||||||
|
export_destination=settings.EXPORT_DESTINATIONS,
|
||||||
|
well_choices = models.Well.objects.order_by('name').all(),
|
||||||
|
|
||||||
**ctx
|
**ctx
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _get_reduct_client() -> ReductStoreClient:
|
def get_active_experiments(session, expid=None):
|
||||||
"""Instancie le client ReductStore depuis les settings Django."""
|
if session:
|
||||||
return ReductStoreClient(url=settings.REDUCTSTORE_URL, token=settings.REDUCTSTORE_TOKEN)
|
experiments = models.SessionExperiment.experiment_by_session(session.id, active=True) or []
|
||||||
|
eid = [str(e.id) for e in experiments]
|
||||||
|
|
||||||
|
print(f"Found {eid} active experiments for session {session.id}")
|
||||||
|
|
||||||
|
if experiments and not expid or expid not in eid:
|
||||||
|
return experiments, experiments[0]
|
||||||
|
|
||||||
|
for e in experiments:
|
||||||
|
if expid == str(e.id):
|
||||||
|
return experiments, e
|
||||||
|
return [], None
|
||||||
|
|
||||||
|
|
||||||
|
def get_experiment_config(request):
|
||||||
|
cursid = request.POST.get('_sid')
|
||||||
|
expid = request.POST.get('_expid')
|
||||||
|
|
||||||
|
print(request.POST)
|
||||||
|
|
||||||
|
current_session = models.Session.get_session(cursid)
|
||||||
|
experiments, current_experiment = get_active_experiments(current_session, expid)
|
||||||
|
|
||||||
|
qs = ExperimentConfig.objects
|
||||||
|
if not current_session:
|
||||||
|
qs = qs.filter(experiment__session_experiments__isnull=False).select_related()
|
||||||
|
elif current_experiment:
|
||||||
|
qs = qs.filter(experiment_id=current_experiment.id)
|
||||||
|
|
||||||
|
context = dict(
|
||||||
|
current_session = current_session,
|
||||||
|
current_experiment = current_experiment,
|
||||||
|
experiments=experiments or [],
|
||||||
|
sessions=models.Session.objects.filter(active=True).all(),
|
||||||
|
)
|
||||||
|
return qs.all(), context
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Vue : liste des configurations
|
# Vue : liste des configurations
|
||||||
@@ -65,19 +113,22 @@ class ExperimentConfigListView(ListView):
|
|||||||
context_object_name = "configs"
|
context_object_name = "configs"
|
||||||
ordering = ["-created_at"]
|
ordering = ["-created_at"]
|
||||||
|
|
||||||
'''
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
# Récupérer le queryset de base
|
qs, self.config_context = get_experiment_config(self.request)
|
||||||
queryset = ExperimentConfig.objects.filter(experiment__session_experiments__isnull=False).select_related()
|
return qs
|
||||||
return queryset'''
|
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
context.update(self.config_context)
|
||||||
return global_context(self.request, **context)
|
return global_context(self.request, **context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Vue : création / modification d'une configuration
|
# Vue : création / modification d'une configuration
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -89,6 +140,10 @@ class ExperimentConfigFormView(FormView):
|
|||||||
form_class = ExperimentConfigForm
|
form_class = ExperimentConfigForm
|
||||||
|
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs, self.config_context = get_experiment_config(self.request)
|
||||||
|
return qs
|
||||||
|
|
||||||
def get_form(self, form_class=None):
|
def get_form(self, form_class=None):
|
||||||
pk = self.kwargs.get("pk")
|
pk = self.kwargs.get("pk")
|
||||||
if pk:
|
if pk:
|
||||||
@@ -102,15 +157,13 @@ class ExperimentConfigFormView(FormView):
|
|||||||
return redirect("planarian:experiment-list")
|
return redirect("planarian:experiment-list")
|
||||||
|
|
||||||
def form_invalid(self, form):
|
def form_invalid(self, form):
|
||||||
# Called when form validation fails
|
|
||||||
print(f"Form validation failed: {form.errors}")
|
print(f"Form validation failed: {form.errors}")
|
||||||
messages.error(self.request, form.errors)
|
messages.error(self.request, form.errors)
|
||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
#def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
# # Custom logic before processing the form
|
print(f"Received POST data: {request.POST}")
|
||||||
# print(f"Received POST data: {request.POST}")
|
return super().post(request, *args, **kwargs)
|
||||||
# return response
|
|
||||||
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
@@ -136,6 +189,10 @@ class ImportParamsView(FormView):
|
|||||||
template_name = "planarian/import_params.html"
|
template_name = "planarian/import_params.html"
|
||||||
form_class = CsvImportForm
|
form_class = CsvImportForm
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs, self.config_context = get_experiment_config(self.request)
|
||||||
|
return qs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
rows = form.csv_rows
|
rows = form.csv_rows
|
||||||
overwrite = form.cleaned_data["overwrite"]
|
overwrite = form.cleaned_data["overwrite"]
|
||||||
@@ -235,6 +292,8 @@ class ExportCsvView(FormView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
qs, config_context = get_experiment_config(self.request)
|
||||||
|
context.update(config_context)
|
||||||
return global_context(self.request, choice_title=_("Tracking depuis ReductStore vers un fichier CSV"), **context)
|
return global_context(self.request, choice_title=_("Tracking depuis ReductStore vers un fichier CSV"), **context)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class ConfigurationAdmin(admin.ModelAdmin):
|
|||||||
"classes": ("collapse",),
|
"classes": ("collapse",),
|
||||||
}),
|
}),
|
||||||
(_("Calibration / Balayage"), {
|
(_("Calibration / Balayage"), {
|
||||||
"fields": ("calibration_crop_radius", "calibration_default_multiwell", "calibration_default_feed", "calibration_default_step", "calibration_default_duration"),
|
"fields": ("tube_axis", "calibration_crop_radius", "calibration_default_multiwell", "calibration_default_feed", "calibration_default_step", "calibration_default_duration"),
|
||||||
"classes": ("collapse",),
|
"classes": ("collapse",),
|
||||||
}),
|
}),
|
||||||
(_("Tracking: valeurs par défaut"), {
|
(_("Tracking: valeurs par défaut"), {
|
||||||
@@ -101,7 +101,7 @@ class SessionExperimentInlineAdmin(admin.TabularInline):
|
|||||||
class SessionAdmin(admin.ModelAdmin):
|
class SessionAdmin(admin.ModelAdmin):
|
||||||
list_filter = ('author',)
|
list_filter = ('author',)
|
||||||
inlines = (SessionExperimentInlineAdmin, )
|
inlines = (SessionExperimentInlineAdmin, )
|
||||||
list_display = ('name', 'author', 'created', 'finished', 'active', 'expected_export', 'expected_scanning', )
|
list_display = ('name', 'id', 'author', 'created', 'finished', 'active', 'expected_export', 'expected_scanning', )
|
||||||
readonly_fields = (
|
readonly_fields = (
|
||||||
'created',
|
'created',
|
||||||
'finished',
|
'finished',
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ VIDEO_TYPE = [
|
|||||||
CAPTURE_TYPE = [
|
CAPTURE_TYPE = [
|
||||||
('rpi', _("Arducam")),
|
('rpi', _("Arducam")),
|
||||||
('webcam', _("Webcam")),
|
('webcam', _("Webcam")),
|
||||||
('file', _("mp4")),
|
('file', _("Fichier vidéo (mp4, avi)")),
|
||||||
]
|
]
|
||||||
|
|
||||||
TUBE_AXIS_TYPE = [
|
TUBE_AXIS_TYPE = [
|
||||||
@@ -250,7 +250,7 @@ class Experiment(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.identifier = slugify(f'{self.created.isoformat()[:19]}-{self.id}-{self.multiwell.position}')
|
self.identifier = f'{self.id}-{self.multiwell.position}_{self.created.isoformat()[:19]}'
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ class Experiment(models.Model):
|
|||||||
verbose_name_plural = _("Expériences")
|
verbose_name_plural = _("Expériences")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.id}:{self.title}-{self.created}'
|
return f'{self.identifier}'
|
||||||
|
|
||||||
|
|
||||||
class Session(models.Model):
|
class Session(models.Model):
|
||||||
|
|||||||
@@ -11,12 +11,11 @@
|
|||||||
<a href="#" class="w3-bar-item w3-mobile w3-btn w3-hover-opacity w3-right w3-xlarge w3-text-green" onclick="goFullscreen()" title="{% trans 'Ecran plein' %}">
|
<a href="#" class="w3-bar-item w3-mobile w3-btn w3-hover-opacity w3-right w3-xlarge w3-text-green" onclick="goFullscreen()" title="{% trans 'Ecran plein' %}">
|
||||||
<i class="fa-solid fa-maximize"></i>
|
<i class="fa-solid fa-maximize"></i>
|
||||||
</a>
|
</a>
|
||||||
{% if request.user.is_superuser %}
|
|
||||||
<div class="w3-dropdown-hover w3-right">
|
|
||||||
<button class="w3-button w3-dark-plus">☰ <span>{% trans "Administrations" %}</span></button>
|
|
||||||
<div class="w3-dropdown-content w3-bar-block w3-border w3-dark-low" style="right:.25em;">
|
|
||||||
<hr class="w3-bar-item divider w3-grey">
|
|
||||||
|
|
||||||
|
<div class="w3-dropdown-hover w3-right">
|
||||||
|
<button class="w3-button w3-dark-plus">☰ <span>{{ request.user.username }}</span></button>
|
||||||
|
<div class="w3-dropdown-content w3-bar-block w3-border w3-dark-low" style="right:.25em;">
|
||||||
|
{% if request.user.is_superuser %}
|
||||||
<a href="{% url 'scanner:admin' %}" class="w3-bar-item w3-btn w3-hover-opacity" title="{% trans "Accès django" %}">
|
<a href="{% url 'scanner:admin' %}" class="w3-bar-item w3-btn w3-hover-opacity" title="{% trans "Accès django" %}">
|
||||||
<i class="fa-solid fa-gear w3-text-pink"></i> {% trans "Base de données" %}
|
<i class="fa-solid fa-gear w3-text-pink"></i> {% trans "Base de données" %}
|
||||||
</a>
|
</a>
|
||||||
@@ -37,7 +36,7 @@
|
|||||||
<a href="{% url 'scanner:logs_worker' %}" class="w3-bar-item w3-btn w3-hover-opacity" target="_blank">
|
<a href="{% url 'scanner:logs_worker' %}" class="w3-bar-item w3-btn w3-hover-opacity" target="_blank">
|
||||||
<i class="fa-regular fa-file-lines w3-text-amber"></i> {% trans "Logs des services" %}
|
<i class="fa-regular fa-file-lines w3-text-amber"></i> {% trans "Logs des services" %}
|
||||||
</a>
|
</a>
|
||||||
|
{% endif %}
|
||||||
<hr class="w3-bar-item divider w3-grey">
|
<hr class="w3-bar-item divider w3-grey">
|
||||||
<form method="post" action="{% url 'logout' %}">
|
<form method="post" action="{% url 'logout' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@@ -47,7 +46,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block sidebar %}
|
{% block sidebar %}
|
||||||
@@ -57,12 +56,6 @@
|
|||||||
<span class="w3-tiny">{{ app_sub_title }}</span>
|
<span class="w3-tiny">{{ app_sub_title }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" action="{% url 'logout' %}" class="w3-padding-small">
|
|
||||||
{% csrf_token %}
|
|
||||||
<button class="w3-bar-item w3-btn w3-hover-opacity" type="submit" title="{% trans 'Déconnexion' %}">
|
|
||||||
{{ request.user.username }}<i class="fa-solid fa-right-from-bracket w3-right"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
{% block system %}
|
{% block system %}
|
||||||
<div class="w3-light-blue w3-padding-large">
|
<div class="w3-light-blue w3-padding-large">
|
||||||
@@ -125,6 +118,7 @@
|
|||||||
<a href="{% url 'scanner:scanning' %}" class="w3-bar-item w3-btn w3-hover-opacity">
|
<a href="{% url 'scanner:scanning' %}" class="w3-bar-item w3-btn w3-hover-opacity">
|
||||||
<i class="fa-solid fa-film w3-text-green w3-xlarge""></i> {% trans "Balayage multi-puits" %}
|
<i class="fa-solid fa-film w3-text-green w3-xlarge""></i> {% trans "Balayage multi-puits" %}
|
||||||
</a>
|
</a>
|
||||||
|
<div class="w3-bar-item w3-dark-xxlight">{% trans "Résultats " %}</div>
|
||||||
<a href="{% url 'scanner:images' %}" class="w3-bar-item w3-btn w3-hover-opacity">
|
<a href="{% url 'scanner:images' %}" class="w3-bar-item w3-btn w3-hover-opacity">
|
||||||
<i class="fa-regular fa-images w3-text-cyan w3-xlarge" w3-xlarge""></i> {% trans "Gestionnaire d'images" %}
|
<i class="fa-regular fa-images w3-text-cyan w3-xlarge" w3-xlarge""></i> {% trans "Gestionnaire d'images" %}
|
||||||
</a>
|
</a>
|
||||||
@@ -142,7 +136,7 @@
|
|||||||
<script>
|
<script>
|
||||||
const stats_endpoint = "{% url 'scanner:api_stats' %}";
|
const stats_endpoint = "{% url 'scanner:api_stats' %}";
|
||||||
</script>
|
</script>
|
||||||
<script src="/static/scanner/js/stats.js"></script>
|
<!--script src="/static/scanner/js/stats.js"></script-->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ def multiwell_cards(sid, experiment):
|
|||||||
row_def = experiment.multiwell.row_def.split(',')
|
row_def = experiment.multiwell.row_def.split(',')
|
||||||
multiwells.append(
|
multiwells.append(
|
||||||
f'''
|
f'''
|
||||||
<div class="w3-center w3-sand">{experiment.title}</div>
|
<div class="w3-padding-small w3-sand">{experiment.title}</div>
|
||||||
<div class="w3-border multiwell_cards">
|
<div class="w3-border multiwell_cards">
|
||||||
''')
|
''')
|
||||||
for row in range(experiment.multiwell.rows):
|
for row in range(experiment.multiwell.rows):
|
||||||
|
|||||||
Reference in New Issue
Block a user