demo / mobile_verification.py
VeuReu's picture
Upload 3 files
867cffa
raw
history blame
12.5 kB
"""
Módulo de verificación por móvil/SMS para usuarios que requieren validación adicional.
Este módulo gestiona:
- Mostrar términos y condiciones de uso
- Solicitar número de teléfono móvil
- Enviar código de verificación por SMS (simulado en demo)
- Verificar código introducido por el usuario
"""
import sys
import random
import streamlit as st
from datetime import datetime
from typing import Optional, Tuple
def log(msg: str):
"""Helper per logging amb timestamp"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
sys.stderr.write(f"[{timestamp}] {msg}\n")
sys.stderr.flush()
def initialize_sms_state():
"""Inicializa el estado de verificación SMS si no existe"""
if 'sms_step' not in st.session_state:
st.session_state.sms_step = 'phone' # 'phone' o 'verify'
if 'sms_code' not in st.session_state:
st.session_state.sms_code = None
if 'sms_phone' not in st.session_state:
st.session_state.sms_phone = None
if 'sms_verified' not in st.session_state:
st.session_state.sms_verified = None # None=pendiente, True=verificado, False=omitido
def send_sms_code(phone_number: str) -> bool:
"""
Simula el envío de código SMS (en producción usaría un servicio real como Twilio).
Args:
phone_number: Número de teléfono completo con código de país
Returns:
True si el envío fue exitoso, False en caso de error
"""
try:
# Generar código aleatorio de 6 dígitos
code = f"{random.randint(100000, 999999)}"
st.session_state.sms_code = code
log(f"[SMS] Código generado para {phone_number}: {code}")
# En producción aquí iría la llamada a un servicio real de SMS
# Por ejemplo, usando Twilio:
# client = TwilioClient(account_sid, auth_token)
# message = client.messages.create(
# body=f"El teu codi de verificació Veureu és: {code}",
# from_=twilio_phone,
# to=phone_number
# )
return True
except Exception as e:
log(f"[SMS] Error enviando código: {e}")
return False
def get_terms_and_conditions() -> str:
"""Retorna el texto completo de términos y condiciones"""
return """
### **Condicions d'ús del sistema Veureu**
En iniciar sessió i pujar un vídeo al sistema Veureu, l'usuari declara i accepta el següent:
**📋 Declaracions:**
- Declara ser titular dels drets d'ús i difusió del vídeo, o disposar de l'autorització expressa dels titulars.
- Declara que totes les persones identificables en el vídeo han atorgat el seu consentiment informat per a la seva utilització amb finalitats d'accessibilitat i audiodescripció.
- Declara que el vídeo no conté:
- Contingut violent, sexual o d'odi,
- Informació confidencial o de caràcter sensible,
- Imatges de menors sense consentiment parental verificable.
**✅ Acceptacions:**
- Accepta que es processi el vídeo exclusivament amb finalitats de generació i validació d'audiodescripcions, conforme a la normativa UNE-153010:2020 i al RGPD.
- Accepta que el sistema pugui enviar les dades necessàries a proveïdors tecnològics externs (p. ex., models d'IA) que actuen com a encarregats de tractament, sota clàusules contractuals tipus i sense reutilització de dades.
- Accepta que les accions realitzades (pujada, acceptació, validació, revocació) siguin registrades en un sistema immutable (AWS QLDB) mitjançant identificadors no personals.
- Pot exercir en qualsevol moment el seu dret a revocar el consentiment mitjançant el botó "Revocar permisos", el que eliminarà el material audiovisual i deixarà constància de la revocació en el registre.
- Accepta que, fins a la validació interna del material per part de l'equip Veureu, el vídeo romandrà en estat "pendent de validació" i no serà utilitzat públicament.
"""
def render_mobile_verification_screen(username: str, role: str) -> Optional[bool]:
"""
Renderiza la pantalla de verificación por móvil post-login.
Args:
username: Nombre de usuario autenticado
role: Rol del usuario
Returns:
True si el usuario completó la verificación
False si omitió la verificación (solo modo análisis)
None si aún está en proceso
"""
st.title("Veureu — Audiodescripció")
st.markdown(f"Benvingut/da, **{username}**")
# Mensaje explicativo según el rol
if role in ["groc", "blau"]:
st.info("""
### 📱 Verificació per WhatsApp/SMS requerida
Per accedir a les funcions completes del sistema (pujar vídeos, validar contingut),
cal verificar el seu número de mòbil i acceptar les condicions d'ús.
**Si únicament vol analitzar i modificar les audiodescripcions existents, pot ometre aquest pas.**
""")
# Ventana desplegable con términos y verificación SMS
with st.expander("📋 Verificació per WhatsApp/SMS", expanded=True):
# Términos y condiciones
st.markdown(get_terms_and_conditions())
st.markdown("---")
st.markdown("#### 📱 Dades de verificació")
# Selector de país y número de teléfono
col_country, col_phone = st.columns([1, 3])
with col_country:
country_code = st.selectbox("País", [
("🇪🇸 +34", "+34"),
("🇫🇷 +33", "+33"),
("🇬🇧 +44", "+44"),
("🇩🇪 +49", "+49"),
("🇮🇹 +39", "+39")
], format_func=lambda x: x[0])[1]
with col_phone:
phone_number = st.text_input(
"Número de telèfon" + (" (opcional)" if role == "groc" else ""),
placeholder="600 123 000",
max_chars=15
)
# Checkbox para aceptar términos
accept_terms = st.checkbox(
"✅ Accepto les condicions d'ús i la política de privadesa",
key="mobile_accept_terms"
)
# Botón para enviar código
if st.button(
"📤 Enviar codi de verificació",
type="primary",
disabled=not (phone_number and accept_terms)
):
full_phone = f"{country_code}{phone_number}"
if send_sms_code(full_phone):
st.session_state.sms_step = 'verify'
st.session_state.sms_phone = full_phone
st.success(f"✅ Codi enviat a {full_phone}")
st.rerun()
else:
st.error("❌ Error enviant el codi. Torna-ho a intentar.")
# Si se envió el SMS, mostrar campo para verificar código
if st.session_state.sms_step == 'verify':
st.markdown("#### 🔓 Verificació del codi")
# Mostrar código en modo demo
if st.session_state.sms_code:
st.info(f"💡 **Mode demo**: El codi és '{st.session_state.sms_code}'")
col_code, col_resend = st.columns([2, 1])
with col_code:
verification_code = st.text_input(
"Codi de 6 dígits",
max_chars=6,
placeholder="000000",
key="mobile_verification_code"
)
with col_resend:
st.markdown("<br>", unsafe_allow_html=True)
if st.button("🔄 Reenviar", key="mobile_resend"):
if send_sms_code(st.session_state.sms_phone):
st.success("✅ Nou codi enviat")
else:
st.error("❌ Error enviant el codi")
if st.button("🔓 Verificar i continuar", type="primary"):
if verification_code == st.session_state.sms_code:
st.session_state.sms_verified = True
st.session_state.sms_phone_verified = st.session_state.sms_phone
# Resetear estado SMS
st.session_state.sms_step = 'phone'
st.session_state.sms_code = None
st.session_state.sms_phone = None
st.success("✅ Verificació completada! Ara pot accedir a totes les funcions.")
log(f"[SMS] Usuario {username} verificado correctamente")
st.rerun()
return True
else:
st.error(f"❌ Codi incorrecte. El codi correcte és: {st.session_state.sms_code}")
# Botones de acción principales
st.markdown("---")
col_continue, col_skip = st.columns([1, 1])
with col_continue:
if st.session_state.sms_verified is True:
if st.button("🚪 Accedir amb permisos complets", type="primary"):
st.success("✅ Accedint a l'aplicació amb permisos complets...")
st.rerun()
return True
with col_skip:
# Solo mostrar botón de omitir para roles que no requieren verificación obligatoria
if role in ["groc", "blau"]:
if st.button("⏭️ Continuar sense verificació (només anàlisi)"):
st.session_state.sms_verified = False
st.info("✅ Sessió iniciada. Pot accedir a les funcions d'anàlisi d'audiodescripcions.")
log(f"[SMS] Usuario {username} omitió verificación SMS")
st.rerun()
return False
return None
def get_user_permissions(role: str, sms_verified: Optional[bool]) -> dict:
"""
Retorna los permisos del usuario según su rol y estado de verificación SMS.
Args:
role: Rol del usuario (verd, groc, blau, taronja, vermell)
sms_verified: Estado de verificación SMS (True, False, None)
Returns:
Diccionario con permisos booleanos
"""
# Permisos base por rol (sin verificación SMS)
base_permissions = {
"verd": {
"analizar": True,
"procesar_videos": True,
"valorar": True,
"validar": True,
"estadisticas": True,
"requires_sms": False
},
"groc": {
"analizar": True,
"procesar_videos": True, # Requiere SMS si no está verificado
"valorar": True,
"validar": False,
"estadisticas": True,
"requires_sms": True
},
"blau": {
"analizar": True,
"procesar_videos": False, # No puede subir videos nunca
"valorar": True,
"validar": True, # Requiere SMS si no está verificado
"estadisticas": True,
"requires_sms": True
},
"taronja": {
"analizar": True,
"procesar_videos": False,
"valorar": True,
"validar": False,
"estadisticas": True,
"requires_sms": False
},
"vermell": {
"analizar": True,
"procesar_videos": False,
"valorar": False,
"validar": False,
"estadisticas": True,
"requires_sms": False
}
}
permissions = base_permissions.get(role, base_permissions["vermell"]).copy()
# Ajustar permisos según verificación SMS
if permissions["requires_sms"]:
if sms_verified is False: # Omitió verificación
# Solo análisis permitido
permissions["procesar_videos"] = False
permissions["validar"] = False
elif sms_verified is None: # Pendiente de verificación
# Pendiente: no dar acceso a funciones que requieren SMS
permissions["procesar_videos"] = False
permissions["validar"] = False
# Si sms_verified is True, mantener permisos originales
return permissions
def show_verification_status_in_sidebar():
"""Muestra el estado de verificación SMS en la barra lateral"""
if st.session_state.get('sms_verified') is True:
st.success("📱 Verificació SMS activada")
elif st.session_state.get('sms_verified') is False:
st.warning("⚠️ Només mode anàlisi")
else:
st.info("⏳ Verificació pendent")