export
This commit is contained in:
@@ -144,6 +144,10 @@ def _copy_to_destinations(source_path: str, filename: str) -> dict:
|
||||
Retourne un dict avec les chemins effectivement écrits.
|
||||
"""
|
||||
results = {"local": None, "remote": None}
|
||||
|
||||
filename = filename.replace(':', '_')
|
||||
|
||||
logger.info("%s %s", source_path, filename)
|
||||
|
||||
for dest in settings.EXPORT_DESTINATIONS:
|
||||
|
||||
@@ -151,10 +155,10 @@ def _copy_to_destinations(source_path: str, filename: str) -> dict:
|
||||
# Déjà sur place, rien à copier
|
||||
results["local"] = source_path
|
||||
|
||||
elif dest == "remote":
|
||||
remote_path = os.path.join(settings.EXPORT_REMOTE_DIR, filename)
|
||||
elif dest == "remote":
|
||||
remote_path = os.path.join(settings.EXPORT_REMOTE_PATH, filename)
|
||||
try:
|
||||
if not remote_mount_available(settings.EXPORT_REMOTE_DIR):
|
||||
if not remote_mount_available(settings.EXPORT_REMOTE_PATH):
|
||||
logger.warning("Partage Samba non disponible, copie ignorée")
|
||||
results["remote_error"] = "Montage indisponible"
|
||||
continue
|
||||
@@ -248,6 +252,7 @@ async def export_images_zip(
|
||||
# --- Génération du ZIP ---
|
||||
max_zip_bytes = max_zip_size_mb * 1024 * 1024 if max_zip_size_mb > 0 else 0
|
||||
ts_s = unix_timestamp_to_iso(start_ts)
|
||||
|
||||
zip_filename = f"{uuid}_{ts_s}.zip"
|
||||
zip_path = os.path.join(settings.EXPORTS_LOCAL_PATH, 'images', zip_filename)
|
||||
os.makedirs(os.path.dirname(zip_path), exist_ok=True)
|
||||
@@ -297,14 +302,16 @@ async def export_images_zip(
|
||||
current_offset += size
|
||||
|
||||
## Copie vers les destinations (local + Samba)
|
||||
#destinations = _copy_to_destinations(zip_path, zip_filename)
|
||||
filename = os.path.join('images', zip_filename)
|
||||
|
||||
destinations = _copy_to_destinations(zip_path, filename)
|
||||
return {
|
||||
"status": "success",
|
||||
"zip_path": zip_path,
|
||||
"frames_written": written,
|
||||
"frames_skipped": skipped,
|
||||
"jpeg_quality": jpeg_quality,
|
||||
#"destinations": destinations,
|
||||
"destinations": destinations,
|
||||
}
|
||||
|
||||
except Exception as exc:
|
||||
@@ -461,8 +468,10 @@ async def export_video_mp4(
|
||||
return {"status": "error", "message": f"Fichier {opencv_video_type} non créé"}
|
||||
|
||||
## Copie vers les destinations (local + Samba)
|
||||
#filename = os.path.basename(video_path)
|
||||
#destinations = _copy_to_destinations(video_path, filename)
|
||||
filename = os.path.basename(video_path)
|
||||
filename = os.path.join('videos', filename)
|
||||
|
||||
destinations = _copy_to_destinations(video_path, filename)
|
||||
return {
|
||||
"status": "success",
|
||||
"video_path": video_path,
|
||||
@@ -470,7 +479,7 @@ async def export_video_mp4(
|
||||
"frames_skipped": skipped,
|
||||
"frame_rate": frame_rate,
|
||||
"file_size_mb": round(os.path.getsize(video_path) / 1024 / 1024, 2),
|
||||
#"destinations": destinations,
|
||||
"destinations": destinations,
|
||||
}
|
||||
|
||||
except Exception as exc:
|
||||
|
||||
@@ -227,6 +227,7 @@ class Experiment(models.Model):
|
||||
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)
|
||||
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created', ]
|
||||
@@ -270,7 +271,12 @@ class Session(models.Model):
|
||||
null=True, blank=True, on_delete=models.SET_NULL, related_name="scanning_session")
|
||||
scanning_finished_at = models.DateTimeField(_("Balayage terminé à"), null=True, blank=True)
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_session(self, sid):
|
||||
return Session.objects.filter(pk=sid).first()
|
||||
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created', ]
|
||||
verbose_name = _("Session d'expérience")
|
||||
|
||||
@@ -9,19 +9,26 @@
|
||||
<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="">
|
||||
{% csrf_token %}
|
||||
<select id="_sid" name="_sid" class="w3-select w3-margin-bottom" onchange="this.form.submit()">
|
||||
<option value="0">---- {% trans "Session d'expérience" %}</option>
|
||||
<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>
|
||||
{% for s in sessions %}
|
||||
<option value="{{ s.id }}" {% if s.id == cursid %}selected{% endif %}>{{ s.name }}</option>
|
||||
<option value="{{ s.id }}" {% if s.id == current_session.id %}selected{% endif %}>{{ s.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if cursid %}
|
||||
{% multiwell_cards cursid experiments %}
|
||||
<div class="w3-margin-left w3-margin-bottom">
|
||||
{% for ss in experiments %}
|
||||
<input class="" type="radio" name="_expid" value="{{ ss.id }}" {% if ss.id == current_experiment.id %}checked{% endif %} onchange="this.form.submit()" >
|
||||
<label>{{ ss.multiwell }}</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if current_session.id and current_experiment %}
|
||||
{% multiwell_cards current_session.id current_experiment %}
|
||||
{% endif %}
|
||||
</form>
|
||||
{% if cursid %}
|
||||
{% if current_session.id %}
|
||||
<a href="#" class="w3-bar-item w3-btn w3-hover-opacity" onclick="download_all_images()">
|
||||
<i class="fa-solid fa-file-export w3-text-red w3-xlarge"></i> {% trans "Exporter l'ensemble des images" %}
|
||||
<i class="fa-solid fa-file-export w3-text-red w3-xlarge"></i> {% trans "Exporter l'ensemble des images" %}<br>
|
||||
<span class="w3-margin-left">{{ export_destination }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -95,14 +102,15 @@
|
||||
action: 'export_images',
|
||||
sid: sid,
|
||||
})
|
||||
}).then(res => {
|
||||
console.log(res);
|
||||
|
||||
}).catch(error => {
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(res => {
|
||||
alert(res.msg);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur:', error);
|
||||
});
|
||||
}
|
||||
|
||||
setGridColumns(columns);
|
||||
if (uuid_btn)
|
||||
handleMultiwell(uuid_btn);
|
||||
|
||||
@@ -11,18 +11,26 @@
|
||||
<form method="post" class="w3-bar-item" action="">
|
||||
{% csrf_token %}
|
||||
<select id="_sid" name="_sid" class="w3-select w3-margin-bottom" onchange="this.form.submit()">
|
||||
<option value="0">---- {% trans "Session d'expériences" %}</option>
|
||||
<option value="0">---- {% trans "Session" %}</option>
|
||||
{% for s in sessions %}
|
||||
<option value="{{ s.id }}" {% if s.id == cursid %}selected{% endif %}>{{ s.name }}</option>
|
||||
<option value="{{ s.id }}" {% if s.id == current_session.id %}selected{% endif %}>{{ s.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if cursid %}
|
||||
{% multiwell_cards cursid experiments %}
|
||||
<div class="w3-margin-left w3-margin-bottom">
|
||||
{% for ss in experiments %}
|
||||
<input class="" type="radio" name="_expid" value="{{ ss.id }}" {% if ss.id == current_experiment.id %}checked{% endif %} onchange="this.form.submit()" >
|
||||
<label>{{ ss.multiwell }}</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if current_session.id and current_experiment %}
|
||||
{% multiwell_cards current_session.id current_experiment %}
|
||||
{% endif %}
|
||||
</form>
|
||||
{% if cursid %}
|
||||
{% if current_session.id %}
|
||||
<a href="#" class="w3-bar-item w3-btn w3-hover-opacity" onclick="download_all_videos()">
|
||||
<i class="fa-solid fa-file-export w3-text-red w3-xlarge"></i> {% trans "Exporter l'ensemble des vidéos" %}
|
||||
<i class="fa-solid fa-file-export w3-text-red w3-xlarge"></i> {% trans "Exporter l'ensemble des vidéos" %}<br>
|
||||
<span class="w3-margin-left">{{ export_destination }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -80,9 +88,12 @@
|
||||
action: 'export_videos',
|
||||
sid: sid,
|
||||
})
|
||||
}).then(res => {
|
||||
console.log(res);
|
||||
}).catch(error => {
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(res => {
|
||||
alert(res.msg);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur:', error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,22 +5,19 @@ from django.utils.html import mark_safe
|
||||
register = template.Library()
|
||||
|
||||
@register.simple_tag
|
||||
def multiwell_cards(sid, experiments):
|
||||
def multiwell_cards(sid, experiment):
|
||||
multiwells = []
|
||||
for obs in experiments:
|
||||
row_def = obs.multiwell.row_def.split(',')
|
||||
multiwells.append(
|
||||
f'''
|
||||
<div class="w3-center w3-sand">{obs.title}</div>
|
||||
<div class="w3-border multiwell_cards">
|
||||
''')
|
||||
for row in range(obs.multiwell.rows):
|
||||
for col in range(obs.multiwell.cols):
|
||||
btn = f'{row_def[row]}{col+1}'
|
||||
uuid = f'{sid}-{obs.multiwell.position}-{btn}'
|
||||
multiwells.append(f"""<button id="btn-{uuid}" name="_multiwell" class="multiwell w3-button" value="{uuid}" onclick="this.form.submit()">{btn}</button>""")
|
||||
multiwells.append('''</div>''')
|
||||
row_def = experiment.multiwell.row_def.split(',')
|
||||
multiwells.append(
|
||||
f'''
|
||||
<div class="w3-center w3-sand">{experiment.title}</div>
|
||||
<div class="w3-border multiwell_cards">
|
||||
''')
|
||||
for row in range(experiment.multiwell.rows):
|
||||
for col in range(experiment.multiwell.cols):
|
||||
btn = f'{row_def[row]}{col+1}'
|
||||
uuid = f'{sid}-{experiment.multiwell.position}-{btn}'
|
||||
multiwells.append(f"""<button id="btn-{uuid}" name="_multiwell" class="multiwell w3-button" value="{uuid}" onclick="this.form.submit()">{btn}</button>""")
|
||||
multiwells.append('''</div>''')
|
||||
|
||||
return mark_safe("\n".join(multiwells))
|
||||
|
||||
|
||||
|
||||
@@ -35,14 +35,13 @@ def stats_view(request):
|
||||
def global_context(request, **ctx):
|
||||
default_multiwell = models.MultiWell.objects.filter(default=True).first()
|
||||
conf = ScannerConstants().get()
|
||||
|
||||
print(conf, type(conf))
|
||||
return dict(
|
||||
app_title=settings.APP_TITLE,
|
||||
app_sub_title=settings.APP_SUB_TITLE,
|
||||
domain_server=settings.DOMAIN_SERVER,
|
||||
conf=conf,
|
||||
default_position = default_multiwell.position or 'HD',
|
||||
export_destination=settings.EXPORT_DESTINATIONS,
|
||||
**ctx
|
||||
)
|
||||
|
||||
@@ -97,6 +96,17 @@ def calibration_view(request):
|
||||
return render(request, "scanner/calibration.html", context=global_context(request, **ctx))
|
||||
|
||||
|
||||
def get_not_active_experiments(session, expid=None):
|
||||
if session:
|
||||
experiments = models.SessionExperiment.experiment_by_session(session.id, active=False) or []
|
||||
if experiments and not expid:
|
||||
return experiments, experiments[0]
|
||||
for e in experiments:
|
||||
if expid == str(e.id):
|
||||
return experiments, e
|
||||
return [], None
|
||||
|
||||
|
||||
## images
|
||||
def get_images(uuid):
|
||||
oldest, latest, n, images = 0, 0, 0, []
|
||||
@@ -124,17 +134,21 @@ def get_images(uuid):
|
||||
|
||||
@login_required
|
||||
def images_view(request):
|
||||
cursid, duration, uuid, images = 0, 0, "", []
|
||||
cursid, expid, duration, uuid, images = 0, None, 0, "", []
|
||||
if request.method == 'POST':
|
||||
cursid = request.POST.get('_sid')
|
||||
expid = request.POST.get('_expid')
|
||||
uuid = request.POST.get('_multiwell')
|
||||
duration, images = get_images(uuid)
|
||||
|
||||
current_session = models.Session.get_session(cursid)
|
||||
experiments, current_experiment = get_not_active_experiments(current_session, expid)
|
||||
ctx = dict(
|
||||
choice_title=_("Gestionnaire d'images"),
|
||||
sessions=models.Session.objects.filter(active=False).all(),
|
||||
experiments=models.SessionExperiment.experiment_by_session(cursid, active=False),
|
||||
cursid=int(cursid),
|
||||
experiments=experiments or [],
|
||||
current_session=current_session,
|
||||
current_experiment=current_experiment,
|
||||
images=images,
|
||||
uuid=uuid,
|
||||
duration=duration,
|
||||
@@ -164,20 +178,24 @@ def get_video(uuid):
|
||||
|
||||
@login_required
|
||||
def replay_view(request):
|
||||
cursid, oldest, latest, uuid, image = 0, 0, 0, "", ""
|
||||
cursid, expid, oldest, latest, uuid, image = 0, None, 0, 0, "", ""
|
||||
|
||||
if request.method == 'POST':
|
||||
cursid = request.POST.get('_sid')
|
||||
expid = request.POST.get('_expid')
|
||||
uuid = request.POST.get('_multiwell')
|
||||
if uuid:
|
||||
oldest, latest, image = get_video(uuid)
|
||||
|
||||
|
||||
current_session = models.Session.get_session(cursid)
|
||||
experiments, current_experiment = get_not_active_experiments(current_session, expid)
|
||||
ctx = dict(
|
||||
choice_title=_("Gestionnaire de vidéos"),
|
||||
ws_route=settings.REPLAY_WEBSOCKET_ROUTE,
|
||||
sessions=models.Session.objects.filter(active=False).all(),
|
||||
experiments=models.SessionExperiment.experiment_by_session(cursid, active=False),
|
||||
cursid=int(cursid),
|
||||
experiments=experiments or [],
|
||||
current_session=current_session,
|
||||
current_experiment=current_experiment,
|
||||
image=image,
|
||||
uuid=uuid,
|
||||
oldest=oldest,
|
||||
@@ -193,12 +211,12 @@ def export_api(request):
|
||||
action = data.get("action")
|
||||
|
||||
if action == 'export_images':
|
||||
job_zip = export_all_images(session_id)
|
||||
return JsonResponse({"state": True, "tasks": job_zip})
|
||||
export_all_images(session_id)
|
||||
return JsonResponse({"success": True, "msg": str(_("Images téléchargées"))})
|
||||
elif action == 'export_videos':
|
||||
job_mp4 = export_all_videos(session_id)
|
||||
return JsonResponse({"state": True, "tasks": job_mp4})
|
||||
export_all_videos(session_id)
|
||||
return JsonResponse({"success": True, "msg": str(_("Vidéos téléchargées"))})
|
||||
else:
|
||||
return JsonResponse({"state": False})
|
||||
return JsonResponse({"success": False, "msg": str(_("Erreur d'exportation"))})
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user