| """UI logic for the "Validació" page.""" |
|
|
| from __future__ import annotations |
|
|
| from datetime import datetime |
| from pathlib import Path |
| from typing import Dict |
|
|
| import streamlit as st |
|
|
|
|
| def _build_candidates(runtime_videos: Path) -> Path: |
| candidates = [ |
| runtime_videos, |
| Path(__file__).resolve().parent.parent / "videos", |
| Path.cwd() / "videos", |
| ] |
| for candidate in candidates: |
| if candidate.exists(): |
| return candidate |
| return candidates[0] |
|
|
|
|
| def render_validation_page( |
| compliance_client, |
| runtime_videos: Path, |
| permissions: Dict[str, bool], |
| username: str, |
| ) -> None: |
| if not permissions.get("validar", False): |
| st.warning("⚠️ No tens permisos per accedir a aquesta secció de validació.") |
| st.stop() |
|
|
| st.header("🔍 Validació de Vídeos") |
|
|
| tab_videos, tab_ads = st.tabs(["📹 Validar Vídeos", "🎬 Validar Audiodescripcions"]) |
|
|
| base_dir = _build_candidates(runtime_videos) |
| if not base_dir.exists(): |
| st.info("📝 No s'ha trobat la carpeta **videos**. Crea-la i afegeix-hi subcarpetes amb els teus vídeos.") |
| st.stop() |
|
|
| with tab_videos: |
| st.subheader("📹 Validar Vídeos Pujats") |
|
|
| video_folders = [] |
| for folder in sorted(base_dir.iterdir()): |
| if folder.is_dir() and folder.name != "completed": |
| video_files = list(folder.glob("*.mp4")) + list(folder.glob("*.avi")) + list(folder.glob("*.mov")) |
| if video_files: |
| mod_time = folder.stat().st_mtime |
| fecha = datetime.fromtimestamp(mod_time).strftime("%Y-%m-%d %H:%M") |
| video_folders.append( |
| { |
| "name": folder.name, |
| "path": str(folder), |
| "created_at": fecha, |
| "video_files": video_files, |
| } |
| ) |
|
|
| if not video_folders: |
| st.info("📝 No hi ha vídeos pujats pendents de validació.") |
| else: |
| opciones_video = [f"{video['name']} - {video['created_at']}" for video in video_folders] |
| seleccion = st.selectbox( |
| "Selecciona un vídeo per validar:", |
| opciones_video, |
| index=0 if opciones_video else None, |
| ) |
|
|
| if seleccion: |
| indice = opciones_video.index(seleccion) |
| video_seleccionat = video_folders[indice] |
|
|
| col1, col2 = st.columns([2, 1]) |
|
|
| with col1: |
| st.markdown("### 📹 Informació del Vídeo") |
| st.markdown(f"**Nom:** {video_seleccionat['name']}") |
| st.markdown(f"**Data:** {video_seleccionat['created_at']}") |
| st.markdown(f"**Arxius:** {len(video_seleccionat['video_files'])} vídeos trobats") |
|
|
| if video_seleccionat["video_files"]: |
| video_path = str(video_seleccionat["video_files"][0]) |
| st.markdown("**Vídeo principal:**") |
| st.video(video_path) |
| else: |
| st.warning("⚠️ No s'han trobat arxius de vídeo.") |
|
|
| with col2: |
| st.markdown("### 🔍 Accions de Validació") |
|
|
| col_btn1, col_btn2 = st.columns(2) |
|
|
| with col_btn1: |
| if st.button("✅ Acceptar", type="primary", key=f"accept_video_{video_seleccionat['name']}"): |
| success = compliance_client.record_validator_decision( |
| document_id=f"video_{video_seleccionat['name']}", |
| validator_email=f"{username}@veureu.local", |
| decision="acceptat", |
| comments=f"Vídeo validat per {username}", |
| ) |
| if success: |
| st.success("✅ Vídeo acceptat i registrat al servei de compliance") |
| else: |
| st.error("❌ Error registrant el veredicte") |
|
|
| with col_btn2: |
| if st.button("❌ Rebutjar", type="secondary", key=f"reject_video_{video_seleccionat['name']}" ): |
| success = compliance_client.record_validator_decision( |
| document_id=f"video_{video_seleccionat['name']}", |
| validator_email=f"{username}@veureu.local", |
| decision="rebutjat", |
| comments=f"Vídeo rebutjat per {username}", |
| ) |
| if success: |
| st.success("✅ Vídeo rebutjat i registrat al servei de compliance") |
| else: |
| st.error("❌ Error registrant el veredicte") |
|
|
| with tab_ads: |
| st.subheader("🎬 Validar Audiodescripcions") |
|
|
| videos_con_ad = [] |
| if base_dir.exists(): |
| for folder in sorted(base_dir.iterdir()): |
| if folder.is_dir() and folder.name != "completed": |
| for subfolder_name in ["MoE", "Salamandra"]: |
| subfolder = folder / subfolder_name |
| if subfolder.exists(): |
| ad_files = list(subfolder.glob("*_ad.txt")) + list(subfolder.glob("*_ad.srt")) |
| if ad_files: |
| mod_time = folder.stat().st_mtime |
| fecha = datetime.fromtimestamp(mod_time).strftime("%Y-%m-%d %H:%M") |
| videos_con_ad.append( |
| { |
| "name": folder.name, |
| "path": str(folder), |
| "created_at": fecha, |
| "ad_files": ad_files, |
| "ad_folder": str(subfolder), |
| } |
| ) |
|
|
| if not videos_con_ad: |
| st.info("📝 No hi ha audiodescripcions pendents de validació.") |
| else: |
| videos_ad_ordenats = sorted(videos_con_ad, key=lambda x: x["created_at"], reverse=True) |
| opciones_ad = [f"{video['name']} - {video['created_at']}" for video in videos_ad_ordenats] |
|
|
| seleccion_ad = st.selectbox( |
| "Selecciona una audiodescripció per validar:", |
| opciones_ad, |
| index=0 if opciones_ad else None, |
| ) |
|
|
| if seleccion_ad: |
| indice = opciones_ad.index(seleccion_ad) |
| video_seleccionat = videos_ad_ordenats[indice] |
|
|
| col1, col2 = st.columns([2, 1]) |
|
|
| with col1: |
| st.markdown("### 🎬 Informació de l'Audiodescripció") |
| st.markdown(f"**Vídeo:** {video_seleccionat['name']}") |
| st.markdown(f"**Data:** {video_seleccionat['created_at']}") |
| st.markdown(f"**Carpeta:** {Path(video_seleccionat['ad_folder']).name}") |
| st.markdown(f"**Arxius:** {len(video_seleccionat['ad_files'])} audiodescripcions trobades") |
|
|
| if video_seleccionat["ad_files"]: |
| ad_path = video_seleccionat["ad_files"][0] |
| st.markdown(f"#### 📄 Contingut ({ad_path.name}):") |
| try: |
| texto = ad_path.read_text(encoding="utf-8") |
| except Exception: |
| texto = ad_path.read_text(errors="ignore") |
| st.text_area("Contingut de l'audiodescripció:", texto, height=300, disabled=True) |
| else: |
| st.warning("⚠️ No s'han trobat arxius d'audiodescripció.") |
|
|
| with col2: |
| st.markdown("### 🔍 Accions de Validació") |
|
|
| col_btn1, col_btn2 = st.columns(2) |
|
|
| with col_btn1: |
| if st.button("✅ Acceptar", type="primary", key=f"accept_ad_{video_seleccionat['name']}"): |
| success = compliance_client.record_validator_decision( |
| document_id=f"ad_{video_seleccionat['name']}", |
| validator_email=f"{username}@veureu.local", |
| decision="acceptat", |
| comments=f"Audiodescripció validada per {username}", |
| ) |
| if success: |
| st.success("✅ Audiodescripció acceptada i registrada al servei de compliance") |
| else: |
| st.error("❌ Error registrant el veredicte") |
|
|
| with col_btn2: |
| if st.button("❌ Rebutjar", type="secondary", key=f"reject_ad_{video_seleccionat['name']}" ): |
| success = compliance_client.record_validator_decision( |
| document_id=f"ad_{video_seleccionat['name']}", |
| validator_email=f"{username}@veureu.local", |
| decision="rebutjat", |
| comments=f"Audiodescripció rebutjada per {username}", |
| ) |
| if success: |
| st.success("✅ Audiodescripció rebutjada i registrada al servei de compliance") |
| else: |
| st.error("❌ Error registrant el veredicte") |
|
|
| st.markdown("---") |
| st.markdown("### ℹ️ Informació del Procés de Validació") |
| st.markdown( |
| """ |
| - **Tots els veredictes** es registren al servei de compliance per garantir la traçabilitat |
| - **Cada validació** inclou veredicte, nom del vídeo i validador responsable |
| - **Els registres** compleixen amb la normativa AI Act i GDPR |
| """ |
| ) |
|
|