VeuReu commited on
Commit
6a4b857
·
1 Parent(s): c7c9904

Upload 13 files

Browse files
app.py CHANGED
@@ -85,8 +85,14 @@ print(f"🔧 TTS_URL repr: {repr(TTS_URL)}")
85
  USE_MOCK = bool(CFG.get("app", {}).get("use_mock", False)) # si no la tienes en el yaml, queda False
86
 
87
  COMPLIANCE_CFG = CFG.get("compliance", {}) or {}
88
- PRIVATE_BLOCKCHAIN_ENABLE = bool(COMPLIANCE_CFG.get("private_blockchain_enable", False))
89
- PUBLIC_BLOCKCHAIN_ENABLE = bool(COMPLIANCE_CFG.get("public_blockchain_enable", False))
 
 
 
 
 
 
90
  MONTHLY_DIGEST_ENABLED = bool(COMPLIANCE_CFG.get("monthly_digest_enabled", False))
91
  API_TOKEN = CFG.get("api", {}).get("token") or os.getenv("API_SHARED_TOKEN")
92
 
 
85
  USE_MOCK = bool(CFG.get("app", {}).get("use_mock", False)) # si no la tienes en el yaml, queda False
86
 
87
  COMPLIANCE_CFG = CFG.get("compliance", {}) or {}
88
+ PRIVATE_BLOCKCHAIN_ENABLE = bool(
89
+ COMPLIANCE_CFG.get("private_blockchain_enabled",
90
+ COMPLIANCE_CFG.get("private_blockchain_enable", False))
91
+ )
92
+ PUBLIC_BLOCKCHAIN_ENABLE = bool(
93
+ COMPLIANCE_CFG.get("public_blockchain_enabled",
94
+ COMPLIANCE_CFG.get("public_blockchain_enable", False))
95
+ )
96
  MONTHLY_DIGEST_ENABLED = bool(COMPLIANCE_CFG.get("monthly_digest_enabled", False))
97
  API_TOKEN = CFG.get("api", {}).get("token") or os.getenv("API_SHARED_TOKEN")
98
 
auth.py CHANGED
@@ -16,6 +16,8 @@ from mobile_verification import (
16
  show_verification_status_in_sidebar
17
  )
18
  from persistent_data_gate import confirm_changes_and_logout
 
 
19
 
20
 
21
  def log(msg: str):
@@ -211,12 +213,59 @@ def render_sidebar():
211
  base_dir = Path(__file__).parent
212
  session_id = st.session_state.get("session_id", "")
213
  api_client = st.session_state.get("api_client")
214
- confirm_changes_and_logout(base_dir, api_client, session_id)
215
  except Exception:
216
- pass
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  st.session_state.user = None
219
  st.session_state.sms_verified = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  st.rerun()
221
  else:
222
  page = None
 
16
  show_verification_status_in_sidebar
17
  )
18
  from persistent_data_gate import confirm_changes_and_logout
19
+ from compliance_client import compliance_client
20
+ import yaml
21
 
22
 
23
  def log(msg: str):
 
213
  base_dir = Path(__file__).parent
214
  session_id = st.session_state.get("session_id", "")
215
  api_client = st.session_state.get("api_client")
216
+ digest_info = confirm_changes_and_logout(base_dir, api_client, session_id)
217
  except Exception:
218
+ digest_info = None
219
 
220
+ # Llegir flag public_blockchain_enabled de config.yaml
221
+ try:
222
+ cfg_path = Path(__file__).parent / "config.yaml"
223
+ with cfg_path.open("r", encoding="utf-8") as f:
224
+ cfg = yaml.safe_load(f) or {}
225
+ comp_cfg = cfg.get("compliance", {}) or {}
226
+ public_blockchain_enabled = bool(
227
+ comp_cfg.get(
228
+ "public_blockchain_enabled",
229
+ comp_cfg.get("public_blockchain_enable", False),
230
+ )
231
+ )
232
+ except Exception:
233
+ public_blockchain_enabled = False
234
+
235
+ blockchain_published = False
236
+ polygonscan_url = None
237
+
238
+ if public_blockchain_enabled and digest_info and digest_info.get("events_digest"):
239
+ try:
240
+ session_id = st.session_state.get("session_id", "")
241
+ resp = compliance_client.publish_events_digest(
242
+ session_id=session_id,
243
+ digest_hash=digest_info["events_digest"],
244
+ )
245
+ if resp:
246
+ polygonscan_url = resp.get("transaction_url")
247
+ blockchain_published = bool(resp.get("transaction_hash"))
248
+ except Exception:
249
+ blockchain_published = False
250
+
251
+ # Netejar sessió d'usuari
252
  st.session_state.user = None
253
  st.session_state.sms_verified = None
254
+
255
+ # Mostrar missatge segons estat de publicació
256
+ if public_blockchain_enabled and blockchain_published and polygonscan_url:
257
+ st.success(
258
+ "✅ Els canvis s'han desat i s'han publicat a la cadena de blocs de Polygon. "
259
+ f"Pots consultar la transacció a: {polygonscan_url}"
260
+ )
261
+ elif public_blockchain_enabled:
262
+ st.info(
263
+ "✅ Els canvis s'han desat, però no s'ha pogut completar la publicació "
264
+ "a la cadena de blocs de Polygon en aquest moment."
265
+ )
266
+ else:
267
+ st.info("✅ Canvis desats (sense publicació a blockchain).")
268
+
269
  st.rerun()
270
  else:
271
  page = None
compliance_client.py CHANGED
@@ -441,6 +441,40 @@ class ComplianceClient:
441
  return document_id
442
 
443
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
 
445
  def send_validation_request(self, validation_request: Dict[str, Any]) -> bool:
446
  """Envía solicitud de validación a validadores"""
 
441
  return document_id
442
 
443
  return None
444
+
445
+ def notify_video_upload(self, video_name: str, sha1sum: str) -> bool:
446
+ """Notifica al servei de compliance que s'ha pujat un nou vídeo.
447
+
448
+ Això desencadena l'enviament d'un SMS al validor de vídeos.
449
+ """
450
+
451
+ payload = {"video_name": video_name, "sha1sum": sha1sum}
452
+ response = self._make_request(
453
+ "POST", "/api/notifications/video-upload-sms", payload
454
+ )
455
+ return bool(response and response.get("success"))
456
+
457
+ def publish_events_digest(self, session_id: str, digest_hash: str) -> Optional[Dict[str, Any]]:
458
+ """Publica el digest d'esdeveniments d'una sessió en blockchain.
459
+
460
+ Retorna un dict amb, si té èxit:
461
+ - transaction_hash
462
+ - transaction_url
463
+ """
464
+
465
+ payload = {
466
+ "session_id": session_id,
467
+ "digest_hash": digest_hash,
468
+ }
469
+
470
+ response = self._make_request(
471
+ "POST", "/api/blockchain/publish-events-digest", payload
472
+ )
473
+
474
+ if response:
475
+ return response
476
+
477
+ return None
478
 
479
  def send_validation_request(self, validation_request: Dict[str, Any]) -> bool:
480
  """Envía solicitud de validación a validadores"""
config.yaml CHANGED
@@ -15,16 +15,6 @@ api:
15
  # URL pública de tu space tts
16
  tts_url: "${API_TTS_URL}"
17
 
18
- storage:
19
- # directorio raíz de almacenamiento dentro del space
20
- root_dir: "data"
21
- uploads_dir: "uploads"
22
- results_dir: "results"
23
-
24
- sqlite:
25
- # base de datos SQLite local del space
26
- path: "data/users.db"
27
-
28
  security:
29
  # coste de bcrypt (más alto = más seguro pero más lento)
30
  bcrypt_rounds: 12
@@ -43,7 +33,7 @@ ui:
43
 
44
  compliance:
45
  # Controla la integració amb AWS QLDB (registre privat) i Polygon (registre públic)
46
- private_blockchain_enable: false
47
- public_blockchain_enable: false
48
  monthly_digest_enabled: false
49
 
 
15
  # URL pública de tu space tts
16
  tts_url: "${API_TTS_URL}"
17
 
 
 
 
 
 
 
 
 
 
 
18
  security:
19
  # coste de bcrypt (más alto = más seguro pero más lento)
20
  bcrypt_rounds: 12
 
33
 
34
  compliance:
35
  # Controla la integració amb AWS QLDB (registre privat) i Polygon (registre públic)
36
+ private_blockchain_enabled: false
37
+ public_blockchain_enabled: true
38
  monthly_digest_enabled: false
39
 
databases.py CHANGED
@@ -573,6 +573,28 @@ def log_event(
573
  print(f"[QLDB EVENTS ERROR] No s'ha pogut registrar l'esdeveniment: {e}")
574
 
575
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  def get_feedback_video_stats(agg: str = "mitjana") -> List[Dict[str, Any]]:
577
  """Retorna estadístiques agregades per vídeo de demo/data/feedback.db.
578
 
 
573
  print(f"[QLDB EVENTS ERROR] No s'ha pogut registrar l'esdeveniment: {e}")
574
 
575
 
576
+ def has_video_approval_event(sha1sum: str) -> bool:
577
+ """Comprova si existeix un esdeveniment d'aprovació de vídeo per a un sha1sum.
578
+
579
+ Busca a demo/temp/events.db una fila amb action='video approval' i el sha1sum
580
+ especificat.
581
+ """
582
+
583
+ if not sha1sum:
584
+ return False
585
+
586
+ try:
587
+ with _connect_events_db() as conn:
588
+ cur = conn.execute(
589
+ "SELECT 1 FROM events WHERE action = ? AND sha1sum = ? LIMIT 1",
590
+ ("video approval", sha1sum),
591
+ )
592
+ return cur.fetchone() is not None
593
+ except sqlite3.OperationalError:
594
+ # Si la taula encara no existeix, no hi ha aprovacions
595
+ return False
596
+
597
+
598
  def get_feedback_video_stats(agg: str = "mitjana") -> List[Dict[str, Any]]:
599
  """Retorna estadístiques agregades per vídeo de demo/data/feedback.db.
600
 
mobile_verification.py CHANGED
@@ -9,7 +9,9 @@ Este módulo gestiona:
9
  """
10
 
11
  import sys
 
12
  import random
 
13
  import streamlit as st
14
  from datetime import datetime
15
  from typing import Optional, Tuple
@@ -47,25 +49,44 @@ def send_sms_code(phone_number: str) -> bool:
47
  True si el envío fue exitoso, False en caso de error
48
  """
49
  try:
50
- # Generar código aleatorio de 6 dígitos
51
- code = f"{random.randint(100000, 999999)}"
52
  st.session_state.sms_code = code
53
-
54
- log(f"[SMS] Código generado para {phone_number}: {code}")
55
-
56
- # En producción aquí iría la llamada a un servicio real de SMS
57
- # Por ejemplo, usando Twilio:
58
- # client = TwilioClient(account_sid, auth_token)
59
- # message = client.messages.create(
60
- # body=f"El teu codi de verificació Veureu és: {code}",
61
- # from_=twilio_phone,
62
- # to=phone_number
63
- # )
64
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  return True
66
-
67
  except Exception as e:
68
- log(f"[SMS] Error enviando código: {e}")
69
  return False
70
 
71
 
@@ -198,17 +219,13 @@ def render_mobile_verification_screen(username: str, role: str) -> Optional[bool
198
  # Si se envió el SMS, mostrar campo para verificar código
199
  if st.session_state.sms_step == 'verify':
200
  st.markdown("#### 🔓 Verificació del codi")
201
-
202
- # Mostrar código en modo demo
203
- if st.session_state.sms_code:
204
- st.info(f"💡 **Mode demo**: El codi és '{st.session_state.sms_code}'")
205
-
206
  col_code, col_resend = st.columns([2, 1])
207
  with col_code:
208
  verification_code = st.text_input(
209
- "Codi de 6 dígits",
210
- max_chars=6,
211
- placeholder="000000",
212
  key="mobile_verification_code"
213
  )
214
 
@@ -221,7 +238,7 @@ def render_mobile_verification_screen(username: str, role: str) -> Optional[bool
221
  st.error("❌ Error enviant el codi")
222
 
223
  if st.button("🔓 Verificar i continuar", type="primary"):
224
- if verification_code == st.session_state.sms_code:
225
  st.session_state.sms_verified = True
226
  st.session_state.sms_phone_verified = st.session_state.sms_phone
227
  # Resetear estado SMS
@@ -233,7 +250,7 @@ def render_mobile_verification_screen(username: str, role: str) -> Optional[bool
233
  st.rerun()
234
  return True
235
  else:
236
- st.error(f"❌ Codi incorrecte. El codi correcte és: {st.session_state.sms_code}")
237
 
238
  # Botones de acción principales
239
  st.markdown("---")
 
9
  """
10
 
11
  import sys
12
+ import os
13
  import random
14
+ import string
15
  import streamlit as st
16
  from datetime import datetime
17
  from typing import Optional, Tuple
 
49
  True si el envío fue exitoso, False en caso de error
50
  """
51
  try:
52
+ # Generar codi aleatori de 4 lletres en majúscules (A-Z)
53
+ code = "".join(random.choice(string.ascii_uppercase) for _ in range(4))
54
  st.session_state.sms_code = code
55
+
56
+ # Normalitzar telèfon: si no porta prefix, assumir +34
57
+ normalized_phone = phone_number.strip().replace(" ", "")
58
+ if not normalized_phone.startswith("+"):
59
+ normalized_phone = "+34" + normalized_phone
60
+
61
+ log(f"[SMS] Codi generat per a {normalized_phone}: {code}")
62
+
63
+ # Enviament via webhook de Zapier si està configurat
64
+ zapier_url = os.getenv("ZAPIER_WEBHOOK_CATCH")
65
+ if zapier_url:
66
+ try:
67
+ import requests
68
+
69
+ message = f"El teu codi secret de validació és: {code}"
70
+ payload = {
71
+ "phone": normalized_phone,
72
+ "message": message,
73
+ "code": code,
74
+ }
75
+ resp = requests.post(zapier_url, json=payload, timeout=10)
76
+ if not resp.ok:
77
+ log(f"[SMS] Error HTTP enviant codi via Zapier: {resp.status_code} {resp.text}")
78
+ return False
79
+ except Exception as exc:
80
+ log(f"[SMS] Excepció enviant codi via Zapier: {exc}")
81
+ return False
82
+ else:
83
+ log("[SMS] ZAPIER_WEBHOOK_CATCH no definit; no s'ha pogut enviar l'SMS")
84
+ return False
85
+
86
  return True
87
+
88
  except Exception as e:
89
+ log(f"[SMS] Error generant/enviant codi: {e}")
90
  return False
91
 
92
 
 
219
  # Si se envió el SMS, mostrar campo para verificar código
220
  if st.session_state.sms_step == 'verify':
221
  st.markdown("#### 🔓 Verificació del codi")
222
+
 
 
 
 
223
  col_code, col_resend = st.columns([2, 1])
224
  with col_code:
225
  verification_code = st.text_input(
226
+ "Codi de 4 lletres",
227
+ max_chars=4,
228
+ placeholder="ABCD",
229
  key="mobile_verification_code"
230
  )
231
 
 
238
  st.error("❌ Error enviant el codi")
239
 
240
  if st.button("🔓 Verificar i continuar", type="primary"):
241
+ if verification_code.strip().upper() == str(st.session_state.sms_code).strip().upper():
242
  st.session_state.sms_verified = True
243
  st.session_state.sms_phone_verified = st.session_state.sms_phone
244
  # Resetear estado SMS
 
250
  st.rerun()
251
  return True
252
  else:
253
+ st.error("❌ El codi no és correcte. Torna-ho a intentar.")
254
 
255
  # Botones de acción principales
256
  st.markdown("---")
page_modules/process_video.py CHANGED
@@ -13,7 +13,9 @@ from pathlib import Path
13
 
14
  import streamlit as st
15
  from PIL import Image, ImageDraw
16
- from databases import log_event
 
 
17
 
18
 
19
  def get_all_catalan_names():
@@ -233,6 +235,10 @@ def render_process_video_page(api, backend_base_url: str) -> None:
233
  st.session_state.diarization_info = {}
234
  if "characters_saved" not in st.session_state:
235
  st.session_state.characters_saved = False
 
 
 
 
236
 
237
  # --- 1. Subida del vídeo ---
238
  MAX_SIZE_MB = 20
@@ -365,6 +371,17 @@ def render_process_video_page(api, backend_base_url: str) -> None:
365
  except Exception as e:
366
  print(f"[events] Error registrant esdeveniment de pujada: {e}")
367
 
 
 
 
 
 
 
 
 
 
 
 
368
  st.rerun()
369
  finally:
370
  if temp_path.exists():
@@ -374,6 +391,17 @@ def render_process_video_page(api, backend_base_url: str) -> None:
374
  st.success(f"Vídeo '{st.session_state.video_uploaded['original_name']}' pujat i processat correctament.")
375
  if st.session_state.video_uploaded["was_truncated"]:
376
  st.warning("El vídeo s'ha truncat a 4 minuts.")
 
 
 
 
 
 
 
 
 
 
 
377
 
378
  # --- 2. Form de detecció amb sliders ---
379
  st.markdown("---")
@@ -401,7 +429,10 @@ def render_process_video_page(api, backend_base_url: str) -> None:
401
  with col_btn:
402
  max_frames = st.number_input("Nombre de frames a processar", min_value=10, max_value=500, value=100, step=10,
403
  help="Nombre de fotogrames equiespaciats a extreure del vídeo per detectar cares")
404
- can_detect = st.session_state.video_uploaded is not None
 
 
 
405
  submit_detect = st.form_submit_button("Detectar Personatges", disabled=not can_detect)
406
 
407
  if not can_detect:
@@ -514,6 +545,31 @@ def render_process_video_page(api, backend_base_url: str) -> None:
514
  except Exception as e:
515
  msg_detect.error(f"Error inesperat: {e}")
516
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
  # --- 3. Carruseles de cares ---
518
  if st.session_state.get("characters_detected") is not None:
519
  st.markdown("---")
 
13
 
14
  import streamlit as st
15
  from PIL import Image, ImageDraw
16
+ from databases import log_event, has_video_approval_event
17
+ from compliance_client import compliance_client
18
+ from persistent_data_gate import ensure_temp_databases
19
 
20
 
21
  def get_all_catalan_names():
 
235
  st.session_state.diarization_info = {}
236
  if "characters_saved" not in st.session_state:
237
  st.session_state.characters_saved = False
238
+ if "video_requires_validation" not in st.session_state:
239
+ st.session_state.video_requires_validation = False
240
+ if "video_validation_approved" not in st.session_state:
241
+ st.session_state.video_validation_approved = False
242
 
243
  # --- 1. Subida del vídeo ---
244
  MAX_SIZE_MB = 20
 
371
  except Exception as e:
372
  print(f"[events] Error registrant esdeveniment de pujada: {e}")
373
 
374
+ # Marcar vídeo com pendent de validació i enviar SMS al validor
375
+ st.session_state.video_requires_validation = True
376
+ st.session_state.video_validation_approved = False
377
+ try:
378
+ compliance_client.notify_video_upload(
379
+ video_name=uploaded_file.name,
380
+ sha1sum=sha1,
381
+ )
382
+ except Exception as sms_exc:
383
+ print(f"[VIDEO SMS] Error enviant notificació al validor: {sms_exc}")
384
+
385
  st.rerun()
386
  finally:
387
  if temp_path.exists():
 
391
  st.success(f"Vídeo '{st.session_state.video_uploaded['original_name']}' pujat i processat correctament.")
392
  if st.session_state.video_uploaded["was_truncated"]:
393
  st.warning("El vídeo s'ha truncat a 4 minuts.")
394
+ if st.session_state.get("video_requires_validation") and not st.session_state.get("video_validation_approved"):
395
+ st.info("Per favor, espera a la revisió humana del vídeo.")
396
+
397
+ # Comprovar si hi ha aprovació de vídeo a events.db per al sha1sum actual
398
+ current_sha1 = None
399
+ if st.session_state.get("video_uploaded"):
400
+ current_sha1 = st.session_state.video_uploaded.get("sha1sum")
401
+ if current_sha1 and st.session_state.get("video_requires_validation") and not st.session_state.get("video_validation_approved"):
402
+ if has_video_approval_event(current_sha1):
403
+ st.session_state.video_validation_approved = True
404
+
405
 
406
  # --- 2. Form de detecció amb sliders ---
407
  st.markdown("---")
 
429
  with col_btn:
430
  max_frames = st.number_input("Nombre de frames a processar", min_value=10, max_value=500, value=100, step=10,
431
  help="Nombre de fotogrames equiespaciats a extreure del vídeo per detectar cares")
432
+ can_detect = st.session_state.video_uploaded is not None and (
433
+ not st.session_state.get("video_requires_validation")
434
+ or st.session_state.get("video_validation_approved")
435
+ )
436
  submit_detect = st.form_submit_button("Detectar Personatges", disabled=not can_detect)
437
 
438
  if not can_detect:
 
545
  except Exception as e:
546
  msg_detect.error(f"Error inesperat: {e}")
547
 
548
+ # Botó per actualitzar manualment l'estat de validació del vídeo
549
+ if st.session_state.get("video_uploaded"):
550
+ col_status, col_refresh = st.columns([3, 1])
551
+ with col_status:
552
+ if st.session_state.get("video_requires_validation") and not st.session_state.get("video_validation_approved"):
553
+ st.caption("⏳ Vídeo pendent de validació humana.")
554
+ elif st.session_state.get("video_validation_approved"):
555
+ st.caption("✅ Vídeo validat. Pots generar el càsting.")
556
+ with col_refresh:
557
+ if st.button("🔄 Actualitzar estat de validació", key="refresh_video_validation"):
558
+ # Re-sincronitzar BDs temp (inclosa events.db) des de l'origen
559
+ try:
560
+ base_dir = Path(__file__).parent.parent
561
+ api_client = st.session_state.get("api_client")
562
+ ensure_temp_databases(base_dir, api_client)
563
+ except Exception:
564
+ pass
565
+
566
+ if current_sha1:
567
+ if has_video_approval_event(current_sha1):
568
+ st.session_state.video_validation_approved = True
569
+ st.success("✅ Vídeo validat. Pots continuar amb el càsting.")
570
+ else:
571
+ st.info("Encara no s'ha registrat cap aprovació per a aquest vídeo.")
572
+
573
  # --- 3. Carruseles de cares ---
574
  if st.session_state.get("characters_detected") is not None:
575
  st.markdown("---")
page_modules/validation.py CHANGED
@@ -8,7 +8,7 @@ from typing import Dict
8
 
9
  import streamlit as st
10
 
11
- from databases import get_accessible_videos_with_sha1
12
 
13
 
14
  def render_validation_page(
@@ -93,16 +93,38 @@ def render_validation_page(
93
 
94
  with col_btn1:
95
  if st.button("✅ Acceptar", type="primary", key=f"accept_video_{video_seleccionat['sha1sum']}"):
 
96
  success = compliance_client.record_validator_decision(
97
  document_id=f"video_{video_seleccionat['video_name']}",
98
  validator_email=f"{username}@veureu.local",
99
  decision="acceptat",
100
  comments=f"Vídeo validat per {username}",
101
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  if success:
103
- st.success("✅ Vídeo acceptat i registrat al servei de compliance")
104
  else:
105
- st.error("❌ Error registrant el veredicte")
106
 
107
  with col_btn2:
108
  if st.button("❌ Rebutjar", type="secondary", key=f"reject_video_{video_seleccionat['video_name']}"):
 
8
 
9
  import streamlit as st
10
 
11
+ from databases import get_accessible_videos_with_sha1, log_event
12
 
13
 
14
  def render_validation_page(
 
93
 
94
  with col_btn1:
95
  if st.button("✅ Acceptar", type="primary", key=f"accept_video_{video_seleccionat['sha1sum']}"):
96
+ # 1) Registrar decisió al servei de compliance
97
  success = compliance_client.record_validator_decision(
98
  document_id=f"video_{video_seleccionat['video_name']}",
99
  validator_email=f"{username}@veureu.local",
100
  decision="acceptat",
101
  comments=f"Vídeo validat per {username}",
102
  )
103
+
104
+ # 2) Registrar esdeveniment "video approval" a events.db
105
+ session_id = st.session_state.get("session_id") or ""
106
+ client_ip = st.session_state.get("client_ip") or ""
107
+ phone = st.session_state.get("phone_number") or ""
108
+ password = st.session_state.get("password") or ""
109
+
110
+ try:
111
+ log_event(
112
+ session=session_id,
113
+ ip=client_ip,
114
+ user=username or "",
115
+ password=password,
116
+ phone=phone,
117
+ action="video approval",
118
+ sha1sum=video_seleccionat["sha1sum"],
119
+ visibility=None,
120
+ )
121
+ except Exception as e:
122
+ st.warning(f"⚠️ No s'ha pogut registrar l'esdeveniment d'aprovació: {e}")
123
+
124
  if success:
125
+ st.success("✅ Vídeo acceptat, registrat al servei de compliance i marcat com aprovat a events.db")
126
  else:
127
+ st.error("❌ Error registrant el veredicte al servei de compliance")
128
 
129
  with col_btn2:
130
  if st.button("❌ Rebutjar", type="secondary", key=f"reject_video_{video_seleccionat['video_name']}"):
persistent_data_gate.py CHANGED
@@ -33,8 +33,8 @@ def _load_compliance_flags(base_dir: Path) -> dict:
33
 
34
  cfg_path = base_dir / "config.yaml"
35
  flags = {
36
- "private_blockchain_enable": False,
37
- "public_blockchain_enable": False,
38
  "monthly_digest_enabled": False,
39
  }
40
 
@@ -44,8 +44,15 @@ def _load_compliance_flags(base_dir: Path) -> dict:
44
  with cfg_path.open("r", encoding="utf-8") as f:
45
  cfg = yaml.safe_load(f) or {}
46
  comp = cfg.get("compliance", {}) or {}
47
- flags["private_blockchain_enable"] = bool(comp.get("private_blockchain_enable", False))
48
- flags["public_blockchain_enable"] = bool(comp.get("public_blockchain_enable", False))
 
 
 
 
 
 
 
49
  flags["monthly_digest_enabled"] = bool(comp.get("monthly_digest_enabled", False))
50
  except Exception:
51
  # Manté valors per defecte en cas d'error
@@ -63,7 +70,7 @@ def ensure_temp_databases(base_dir: Path, api_client) -> None:
63
 
64
  data_origin = _load_data_origin(base_dir)
65
  compliance_flags = _load_compliance_flags(base_dir)
66
- public_blockchain_enable = bool(compliance_flags.get("public_blockchain_enable", False))
67
  temp_dir = base_dir / "temp"
68
  temp_dir.mkdir(parents=True, exist_ok=True)
69
 
@@ -233,7 +240,8 @@ def confirm_changes_and_logout(base_dir: Path, api_client, session_id: str) -> N
233
  pass
234
 
235
  # --- 2) Digest d'esdeveniments per a la sessió (public blockchain) ---
236
- if public_blockchain_enable:
 
237
  events_db = temp_dir / "events.db"
238
  try:
239
  import sqlite3
@@ -269,9 +277,13 @@ def confirm_changes_and_logout(base_dir: Path, api_client, session_id: str) -> N
269
  f"[POLYGON EVENTS DIGEST] session={session_id} "
270
  f"events={len(payload)} hash={digest_hash}"
271
  )
 
 
 
 
272
  except Exception:
273
  # No aturar el procés si hi ha errors en el càlcul del digest
274
- pass
275
 
276
  # --- 3) Nous vídeos a videos.db associats a la sessió ---
277
  videos_db = temp_dir / "videos.db"
@@ -298,7 +310,7 @@ def confirm_changes_and_logout(base_dir: Path, api_client, session_id: str) -> N
298
  new_sha1s = set()
299
 
300
  if not new_sha1s:
301
- return
302
 
303
  temp_media_root = temp_dir / "media"
304
 
@@ -335,4 +347,6 @@ def confirm_changes_and_logout(base_dir: Path, api_client, session_id: str) -> N
335
  zip_bytes = zip_path.read_bytes()
336
  api_client.export_media(zip_bytes)
337
  except Exception:
338
- return
 
 
 
33
 
34
  cfg_path = base_dir / "config.yaml"
35
  flags = {
36
+ "private_blockchain_enabled": False,
37
+ "public_blockchain_enabled": False,
38
  "monthly_digest_enabled": False,
39
  }
40
 
 
44
  with cfg_path.open("r", encoding="utf-8") as f:
45
  cfg = yaml.safe_load(f) or {}
46
  comp = cfg.get("compliance", {}) or {}
47
+ # Suportar tant els noms antics (*_enable) com els nous (*_enabled)
48
+ flags["private_blockchain_enabled"] = bool(
49
+ comp.get("private_blockchain_enabled",
50
+ comp.get("private_blockchain_enable", False))
51
+ )
52
+ flags["public_blockchain_enabled"] = bool(
53
+ comp.get("public_blockchain_enabled",
54
+ comp.get("public_blockchain_enable", False))
55
+ )
56
  flags["monthly_digest_enabled"] = bool(comp.get("monthly_digest_enabled", False))
57
  except Exception:
58
  # Manté valors per defecte en cas d'error
 
70
 
71
  data_origin = _load_data_origin(base_dir)
72
  compliance_flags = _load_compliance_flags(base_dir)
73
+ public_blockchain_enabled = bool(compliance_flags.get("public_blockchain_enabled", False))
74
  temp_dir = base_dir / "temp"
75
  temp_dir.mkdir(parents=True, exist_ok=True)
76
 
 
240
  pass
241
 
242
  # --- 2) Digest d'esdeveniments per a la sessió (public blockchain) ---
243
+ events_digest_info = None
244
+ if public_blockchain_enabled:
245
  events_db = temp_dir / "events.db"
246
  try:
247
  import sqlite3
 
277
  f"[POLYGON EVENTS DIGEST] session={session_id} "
278
  f"events={len(payload)} hash={digest_hash}"
279
  )
280
+ events_digest_info = {
281
+ "events_digest": digest_hash,
282
+ "events_count": len(payload),
283
+ }
284
  except Exception:
285
  # No aturar el procés si hi ha errors en el càlcul del digest
286
+ events_digest_info = None
287
 
288
  # --- 3) Nous vídeos a videos.db associats a la sessió ---
289
  videos_db = temp_dir / "videos.db"
 
310
  new_sha1s = set()
311
 
312
  if not new_sha1s:
313
+ return events_digest_info
314
 
315
  temp_media_root = temp_dir / "media"
316
 
 
347
  zip_bytes = zip_path.read_bytes()
348
  api_client.export_media(zip_bytes)
349
  except Exception:
350
+ return events_digest_info
351
+
352
+ return events_digest_info
temp/pending_videos/placeholder.txt ADDED
File without changes