Salif SAWADOGO
🔥 resolve end of page musmath
68cc99d
"""
callbacks.py
-------------
Ce module définit les callbacks de l'application Dash en utilisant les abstractions
définies dans le sous-package helpers."""
from dash import Input, Output, State, no_update
import datetime
import os
from pathlib import Path
from loguru import logger
from dotenv import load_dotenv
load_dotenv()
from helpers.processor import Processor
from helpers.s3 import S3Client
from helpers.models import S3Config
from app import app
from global_vars import data, BUCKET_NAME
PERSIST_FILE = "results.json"
# Instanciation du Processor
processor = Processor()
# Instanciation du client S3 à partir de la configuration
s3_config = S3Config(
bucket_name=BUCKET_NAME,
endpoint_url=os.getenv("AWS_ENDPOINT_URL_S3"),
access_key=os.getenv("AWS_ACCESS_KEY_ID"),
secret_key=os.getenv("AWS_SECRET_ACCESS_KEY")
)
s3_client = S3Client(s3_config)
# -----------------------------------------------------------------------------
# CALLBACKS D'AFFICHAGE DES SECTIONS
# -----------------------------------------------------------------------------
@app.callback(
Output("chapter-section", "style"),
Output("pseudo-continue-button", "style"),
Input("pseudo-continue-button", "n_clicks"),
State("user-info", "value")
)
def show_chapter_section(n_clicks, user_info):
"""Affiche la section chapitre après saisie d'une information utilisateur."""
if n_clicks and user_info:
return {"display": "block"}, {"display": "none"}
return {"display": "none"}, {"display": "block"}
@app.callback(
Output("page-section", "style"),
Output("chapter-continue-button", "style"),
Input("chapter-continue-button", "n_clicks"),
State("chapter-dropdown", "value")
)
def show_page_section(n_clicks, chapter_value):
"""Affiche la section page après sélection d'un chapitre."""
if n_clicks and chapter_value:
return {"display": "block"}, {"display": "none"}
return {"display": "none"}, {"display": "block"}
@app.callback(
Output("transcription-section", "style"),
Output("start-button", "style"),
Input("start-button", "n_clicks"),
State("page-dropdown", "value")
)
def show_transcription_section(n_clicks, page_value):
"""Affiche la section transcription après sélection d'une page."""
if n_clicks and page_value:
return {"display": "block"}, {"display": "none"}
return {"display": "none"}, {"display": "block"}
# -----------------------------------------------------------------------------
# CALLBACK DE MISE À JOUR DU DROPDOWN DES PAGES
# -----------------------------------------------------------------------------
@app.callback(
Output("page-dropdown", "options"),
Input("chapter-dropdown", "value")
)
def update_pages(chapter_value):
"""Met à jour les options du dropdown de pages selon le chapitre sélectionné."""
if chapter_value:
chapter_path = Path(chapter_value)
pages = [d for d in chapter_path.iterdir() if d.is_dir()] if chapter_path.exists() else []
return [{"label": d.name, "value": str(d)} for d in pages]
return []
# -----------------------------------------------------------------------------
# CALLBACK DE MISE À JOUR AUDIO ET DES SUGGESTIONS (via dcc.Store)
# -----------------------------------------------------------------------------
@app.callback(
Output("audio-store", "data"),
Output("values-store", "data"),
Output("audio-player", "src"),
Output("suggestion-checklist", "options"),
Output("hidden-message", "style"),
Input("page-dropdown", "value"),
State("chapter-dropdown", "value")
)
def update_audio_and_suggestions(page_value, chapter_value):
"""
Met à jour les stores pour les chemins audio et les suggestions.
Affiche le premier segment audio et les 6 premières suggestions.
"""
hidden_style = {"display": "none"}
if page_value and chapter_value:
# Utilise la méthode abstraite pour charger les transcriptions et extraire l'état
possible_values, audio_paths = processor.load_page_verses_and_audios(s3_client, page_value, data)
options = [{"label": t, "value": t} for t in possible_values[:6]]
if len(audio_paths)>0: #control end of page
audio_src = audio_paths[0]
return audio_paths, possible_values, audio_src, options, hidden_style
hidden_style = {"display": "block"} # Show the hidden message
try:
os.remove(PERSIST_FILE)
except:
pass
return no_update, no_update, no_update, no_update, hidden_style
return no_update, no_update, no_update, no_update, hidden_style
# CALLBACK POUR LE TRAITEMENT DE LA TRANSCIPTION
# -----------------------------------------------------------------------------
@app.callback(
Output("audio-store", "data", allow_duplicate=True),
Output("values-store", "data", allow_duplicate=True),
Output("audio-player", "src", allow_duplicate=True),
Output("suggestion-checklist", "options", allow_duplicate=True),
Output("suggestion-checklist", "value", allow_duplicate=True),
Output("confirmation-message", "children"),
Output("transcription-store", "data"),
Input("submit-button", "n_clicks"),
State("suggestion-checklist", "value"),
State("user-info", "value"),
State("page-dropdown", "value"),
State("audio-player", "src"),
State("audio-store", "data"),
State("values-store", "data"),
State("transcription-store", "data"),
prevent_initial_call=True
)
def update_transcription(n_clicks, selected_transcriptions, user_info, page_value,
current_audio, audio_store, values_store, stored_transcriptions):
"""
Traite la soumission d'une transcription :
- Ajoute l'entrée avec timestamp dans le store de transcription.
- Retire le segment audio traité et les suggestions utilisées.
- Met à jour l'audio et les options de la checklist.
"""
if n_clicks > 0 and page_value and current_audio:
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
stored_transcriptions = stored_transcriptions if stored_transcriptions is not None else []
stored_transcriptions.append({
"segment_path": current_audio,
"transcriptions": selected_transcriptions,
"timestamp": timestamp,
"user_id": user_info
})
# Mise à jour du store audio
if audio_store and isinstance(audio_store, list):
audio_store.pop(0)
next_audio = audio_store[0] if audio_store else ""
else:
next_audio = ""
# Mise à jour du store de suggestions
if values_store and isinstance(values_store, list):
for val in selected_transcriptions:
if val in values_store:
values_store.remove(val)
next_options = [{"label": t, "value": t} for t in (values_store[:6] + ["autre transcription"])] if values_store else ["autre transcription"]
confirmation_message = (f"Transcriptions sélectionnées : {', '.join(selected_transcriptions)}"
if selected_transcriptions else "Aucune transcription sélectionnée.")
# Réinitialisation de la checklist
print(next_options)
print("************")
if (len(next_options)>1):
return audio_store, values_store, next_audio, next_options, [], confirmation_message, stored_transcriptions
return no_update, no_update, no_update, no_update, no_update, "fin de la page, veuillez sauvegarder", stored_transcriptions
# -----------------------------------------------------------------------------
# CALLBACK POUR LA SAUVEGARDE DES RÉSULTATS
# -----------------------------------------------------------------------------
@app.callback(
Output("confirmation-message", "children", allow_duplicate=True),
Input("save-results-button", "n_clicks"),
State("page-dropdown", "value"),
State("transcription-store", "data"),
prevent_initial_call=True
)
def save_results(n_clicks, page_value, stored_transcriptions):
"""
Sauvegarde les transcriptions en combinant les données persistantes existantes
avec les nouvelles et en les uploadant sur S3.
"""
if n_clicks > 0 and page_value:
try:
initial_transcriptions = processor.load_persistent_data(PERSIST_FILE)
except Exception as e:
logger.error(f"Erreur lors du chargement des données persistantes : {e}")
initial_transcriptions = []
combined_transcriptions = initial_transcriptions + (stored_transcriptions if stored_transcriptions else [])
if len(combined_transcriptions)>0:
processor.save_persistent_data(combined_transcriptions, PERSIST_FILE)
cleaned_page = page_value.replace("\\", "/").replace("assets/", "")
s3_key = f"labelling/{cleaned_page}/{PERSIST_FILE}"
s3_client.upload_file(PERSIST_FILE, s3_key)
return "Les résultats ont été sauvegardés avec succès."
return no_update