aimodelshare 0.3.7__py3-none-any.whl → 0.4.71__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aimodelshare/moral_compass/__init__.py +51 -2
- aimodelshare/moral_compass/api_client.py +92 -4
- aimodelshare/moral_compass/apps/__init__.py +36 -16
- aimodelshare/moral_compass/apps/ai_consequences.py +98 -88
- aimodelshare/moral_compass/apps/bias_detective_ca.py +2722 -0
- aimodelshare/moral_compass/apps/bias_detective_en.py +2722 -0
- aimodelshare/moral_compass/apps/bias_detective_part1.py +2722 -0
- aimodelshare/moral_compass/apps/bias_detective_part2.py +2465 -0
- aimodelshare/moral_compass/apps/bias_detective_part_es.py +2722 -0
- aimodelshare/moral_compass/apps/ethical_revelation.py +237 -147
- aimodelshare/moral_compass/apps/fairness_fixer.py +1839 -859
- aimodelshare/moral_compass/apps/fairness_fixer_ca.py +1869 -0
- aimodelshare/moral_compass/apps/fairness_fixer_en.py +1869 -0
- aimodelshare/moral_compass/apps/fairness_fixer_es.py +1869 -0
- aimodelshare/moral_compass/apps/judge.py +130 -143
- aimodelshare/moral_compass/apps/justice_equity_upgrade.py +793 -831
- aimodelshare/moral_compass/apps/justice_equity_upgrade_ca.py +815 -0
- aimodelshare/moral_compass/apps/justice_equity_upgrade_en.py +815 -0
- aimodelshare/moral_compass/apps/justice_equity_upgrade_es.py +815 -0
- aimodelshare/moral_compass/apps/mc_integration_helpers.py +227 -745
- aimodelshare/moral_compass/apps/model_building_app_ca.py +4544 -0
- aimodelshare/moral_compass/apps/model_building_app_ca_final.py +3899 -0
- aimodelshare/moral_compass/apps/model_building_app_en.py +4290 -0
- aimodelshare/moral_compass/apps/model_building_app_en_final.py +3869 -0
- aimodelshare/moral_compass/apps/model_building_app_es.py +4362 -0
- aimodelshare/moral_compass/apps/model_building_app_es_final.py +3899 -0
- aimodelshare/moral_compass/apps/model_building_game.py +4211 -935
- aimodelshare/moral_compass/apps/moral_compass_challenge.py +195 -95
- aimodelshare/moral_compass/apps/what_is_ai.py +126 -117
- aimodelshare/moral_compass/challenge.py +98 -17
- {aimodelshare-0.3.7.dist-info → aimodelshare-0.4.71.dist-info}/METADATA +1 -1
- {aimodelshare-0.3.7.dist-info → aimodelshare-0.4.71.dist-info}/RECORD +35 -19
- aimodelshare/moral_compass/apps/bias_detective.py +0 -714
- {aimodelshare-0.3.7.dist-info → aimodelshare-0.4.71.dist-info}/WHEEL +0 -0
- {aimodelshare-0.3.7.dist-info → aimodelshare-0.4.71.dist-info}/licenses/LICENSE +0 -0
- {aimodelshare-0.3.7.dist-info → aimodelshare-0.4.71.dist-info}/top_level.txt +0 -0
|
@@ -5,6 +5,7 @@ Updated with i18n support and visual fixes (gr.HTML implementation).
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import os
|
|
7
7
|
import gradio as gr
|
|
8
|
+
from functools import lru_cache
|
|
8
9
|
|
|
9
10
|
os.environ.setdefault("APP_NAME", "judge")
|
|
10
11
|
|
|
@@ -23,7 +24,7 @@ TRANSLATIONS = {
|
|
|
23
24
|
which defendants can be safely released.<br><br>
|
|
24
25
|
|
|
25
26
|
To help you, the court has implemented an AI system that predicts the risk of each
|
|
26
|
-
defendant committing new crimes if released. The AI categorizes defendants as:<br><br>
|
|
27
|
+
defendant committing new crimes if released on parole. The AI categorizes defendants as:<br><br>
|
|
27
28
|
|
|
28
29
|
<ul style='font-size:18px;'>
|
|
29
30
|
<li><span class='ai-risk-label risk-high'>High Risk</span> - Likely to re-offend</li>
|
|
@@ -36,7 +37,7 @@ TRANSLATIONS = {
|
|
|
36
37
|
"btn_start": "Begin Making Decisions ▶️",
|
|
37
38
|
"profiles_title": "👥 Defendant Profiles",
|
|
38
39
|
"hint_box": "Review each defendant's information and the AI's risk assessment, then make your decision.",
|
|
39
|
-
"btn_release": "✓ Release
|
|
40
|
+
"btn_release": "✓ Release",
|
|
40
41
|
"btn_keep": "✗ Keep in Prison",
|
|
41
42
|
"btn_show_summary": "📊 Show My Decisions Summary",
|
|
42
43
|
"btn_complete": "Complete This Section ▶️",
|
|
@@ -58,8 +59,8 @@ TRANSLATIONS = {
|
|
|
58
59
|
"decision_keep": "Keep in Prison",
|
|
59
60
|
"decision_recorded": "✓ Decision recorded:",
|
|
60
61
|
"summary_title": "📊 Your Decisions Summary",
|
|
61
|
-
"summary_released": "
|
|
62
|
-
"summary_kept": "
|
|
62
|
+
"summary_released": "Incarcerated Individuals Released:",
|
|
63
|
+
"summary_kept": "Incarcerated Individuals Kept in Prison:",
|
|
63
64
|
"summary_empty": "No decisions made yet.",
|
|
64
65
|
"nav_loading_profiles": "Loading defendant profiles...",
|
|
65
66
|
"nav_reviewing": "Reviewing your decisions...",
|
|
@@ -68,7 +69,7 @@ TRANSLATIONS = {
|
|
|
68
69
|
"label_defendant": "Defendant",
|
|
69
70
|
"label_age": "Age",
|
|
70
71
|
"label_gender": "Gender",
|
|
71
|
-
"label_race": "Race",
|
|
72
|
+
"label_race": "Race/Etnicity",
|
|
72
73
|
"label_prior": "Prior Offenses",
|
|
73
74
|
"label_charge": "Current Charge",
|
|
74
75
|
"label_ai_risk": "🤖 AI Risk Assessment:",
|
|
@@ -90,35 +91,35 @@ TRANSLATIONS = {
|
|
|
90
91
|
"Burglary": "Burglary"
|
|
91
92
|
},
|
|
92
93
|
"es": {
|
|
93
|
-
"title": "⚖️
|
|
94
|
-
"intro_role": "<b>Tu
|
|
94
|
+
"title": "⚖️ ¡Ponte en el rol de juez!",
|
|
95
|
+
"intro_role": "<b>Tu rol:</b> Eres un juez o una jueza que debe decidir si se concede la libertad condicional a una persona presa o si debe continuar en prisión.<br>Un sistema de IA ha analizado cada caso y ha proporcionado una evaluación de riesgos.<br><br><b>Tu tarea:</b> Revisa el perfil de cada acusado y la predicción de la IA, luego toma tu decisión.",
|
|
95
96
|
"loading": "⏳ Cargando...",
|
|
96
|
-
"scenario_title": "📋 El
|
|
97
|
+
"scenario_title": "📋 El escenario",
|
|
97
98
|
"scenario_box": """
|
|
98
|
-
Eres
|
|
99
|
-
qué
|
|
99
|
+
Eres miembro de un tribunal penal con mucho trabajo. Debido al hacinamiento en las prisiones, debes decidir
|
|
100
|
+
qué personas presas pueden obtener la libertad condicional de manera segura.<br><br>
|
|
100
101
|
|
|
101
102
|
Para ayudarte, el tribunal ha implementado un sistema de IA que predice el riesgo de que cada
|
|
102
|
-
|
|
103
|
+
persona presa cometa nuevos delitos si obtiene la libertad condicional. La IA clasifica a las personas presas como:<br><br>
|
|
103
104
|
|
|
104
105
|
<ul style='font-size:18px;'>
|
|
105
|
-
<li><span class='ai-risk-label risk-high'>Alto Riesgo</span> - Probable reincidencia</li>
|
|
106
|
+
<li><span class='ai-risk-label risk-high'>Alto Riesgo</span> - Probable reincidencia (de cometer nuevos delitos)</li>
|
|
106
107
|
<li><span class='ai-risk-label risk-medium'>Riesgo Medio</span> - Probabilidad moderada de reincidencia</li>
|
|
107
108
|
<li><span class='ai-risk-label risk-low'>Bajo Riesgo</span> - Improbable reincidencia</li>
|
|
108
109
|
</ul>
|
|
109
110
|
|
|
110
111
|
<b>Recuerda:</b> Tus decisiones afectan la vida de personas reales y la seguridad pública.
|
|
111
112
|
""",
|
|
112
|
-
"btn_start": "Comenzar a
|
|
113
|
-
"profiles_title": "👥 Perfiles de
|
|
114
|
-
"hint_box": "Revisa la información de cada
|
|
115
|
-
"btn_release": "✓ Liberar
|
|
116
|
-
"btn_keep": "✗ Mantener en
|
|
117
|
-
"btn_show_summary": "📊 Mostrar
|
|
118
|
-
"btn_complete": "Completar esta
|
|
119
|
-
"completion_title": "✅ ¡Decisiones
|
|
120
|
-
"completion_box_pre": "
|
|
121
|
-
"completion_question": "¿Y si la IA
|
|
113
|
+
"btn_start": "Comenzar a tomar decisiones ▶️",
|
|
114
|
+
"profiles_title": "👥 Perfiles de las personas presas",
|
|
115
|
+
"hint_box": "Revisa la información de cada persona presa y la evaluación de riesgos de la IA, luego toma tu decisión.",
|
|
116
|
+
"btn_release": "✓ Liberar la persona presa",
|
|
117
|
+
"btn_keep": "✗ Mantener en prisión",
|
|
118
|
+
"btn_show_summary": "📊 Mostrar resumen de decisiones",
|
|
119
|
+
"btn_complete": "Completar esta sección ▶️",
|
|
120
|
+
"completion_title": "✅ ¡Decisiones completadas!",
|
|
121
|
+
"completion_box_pre": "Ya has tomado tus decisiones basándote en las recomendaciones de la IA.<br><br>Ahora bien, surge una pregunta clave:<br><br>",
|
|
122
|
+
"completion_question": "¿Y si la IA se equivocó?",
|
|
122
123
|
"completion_box_post": """
|
|
123
124
|
<p style='font-size:1.1rem;'>
|
|
124
125
|
Continúa en la siguiente sección para explorar las consecuencias de
|
|
@@ -126,27 +127,27 @@ TRANSLATIONS = {
|
|
|
126
127
|
</p>
|
|
127
128
|
<h1 style='margin:20px 0; font-size: 3rem;'>👇 DESPLÁZATE HACIA ABAJO 👇</h1>
|
|
128
129
|
<p style='font-size:1.1rem;'>
|
|
129
|
-
Encuentra la siguiente sección
|
|
130
|
+
Encuentra la siguiente sección para continuar tu viaje.
|
|
130
131
|
</p>
|
|
131
132
|
""",
|
|
132
|
-
"btn_back": "◀️ Volver a
|
|
133
|
+
"btn_back": "◀️ Volver a revisar decisiones",
|
|
133
134
|
"decision_release": "Liberar",
|
|
134
|
-
"decision_keep": "Mantener en
|
|
135
|
+
"decision_keep": "Mantener en prisión",
|
|
135
136
|
"decision_recorded": "✓ Decisión registrada:",
|
|
136
|
-
"summary_title": "📊 Resumen de tus
|
|
137
|
-
"summary_released": "
|
|
138
|
-
"summary_kept": "
|
|
137
|
+
"summary_title": "📊 Resumen de tus decisiones",
|
|
138
|
+
"summary_released": "Personas presas puestas en libertad:",
|
|
139
|
+
"summary_kept": "Personas presas que continúan en prisión:",
|
|
139
140
|
"summary_empty": "Aún no se han tomado decisiones.",
|
|
140
141
|
"nav_loading_profiles": "Cargando perfiles...",
|
|
141
142
|
"nav_reviewing": "Revisando tus decisiones...",
|
|
142
|
-
"nav_returning": "
|
|
143
|
-
"label_defendant": "
|
|
143
|
+
"nav_returning": "Volver a perfiles...",
|
|
144
|
+
"label_defendant": "Persona presa",
|
|
144
145
|
"label_age": "Edad",
|
|
145
146
|
"label_gender": "Género",
|
|
146
|
-
"label_race": "Raza",
|
|
147
|
-
"label_prior": "Delitos
|
|
148
|
-
"label_charge": "Cargo
|
|
149
|
-
"label_ai_risk": "🤖 Evaluación de
|
|
147
|
+
"label_race": "Raza/Etnicidad",
|
|
148
|
+
"label_prior": "Delitos previos",
|
|
149
|
+
"label_charge": "Cargo actual",
|
|
150
|
+
"label_ai_risk": "🤖 Evaluación de riesgo IA:",
|
|
150
151
|
"label_risk": "Riesgo",
|
|
151
152
|
"label_confidence": "Confianza:",
|
|
152
153
|
"Male": "Masculino",
|
|
@@ -161,37 +162,37 @@ TRANSLATIONS = {
|
|
|
161
162
|
"Theft": "Robo",
|
|
162
163
|
"Assault": "Asalto",
|
|
163
164
|
"Fraud": "Fraude",
|
|
164
|
-
"Burglary": "Robo con allanamiento"
|
|
165
|
+
"Burglary": "Robo con allanamiento de morada"
|
|
165
166
|
},
|
|
166
167
|
"ca": {
|
|
167
|
-
"title": "⚖️
|
|
168
|
-
"intro_role": "<b>El teu
|
|
168
|
+
"title": "⚖️ Posa't en el rol de jutge!",
|
|
169
|
+
"intro_role": "<b>El teu rol:</b> Ets un jutge o una jutgessa que ha de decidir si es concedeix la llibertat condicional a una persona presa o ha de continuar a la presó.<br>Un sistema d'IA ha analitzat cada cas i ha proporcionat una avaluació de riscos.<br><br><b>La teva tasca:</b> Revisa el perfil de cada persona presa i la predicció de la IA, després pren la teva decisió.",
|
|
169
170
|
"loading": "⏳ Carregant...",
|
|
170
|
-
"scenario_title": "📋 L'
|
|
171
|
+
"scenario_title": "📋 L'escenari",
|
|
171
172
|
"scenario_box": """
|
|
172
|
-
Ets
|
|
173
|
-
|
|
173
|
+
Ets membre d'un tribunal penal amb molta feina. A causa de la massificació a les presons, has de decidir
|
|
174
|
+
quines persones preses poden ser posades en llibertat de manera segura.<br><br>
|
|
174
175
|
|
|
175
176
|
Per ajudar-te, el tribunal ha implementat un sistema d'IA que prediu el risc que cada
|
|
176
|
-
|
|
177
|
+
persona presa cometi nous delictes si obté la llibertat condicional. La IA classifica les persones preses com:<br><br>
|
|
177
178
|
|
|
178
179
|
<ul style='font-size:18px;'>
|
|
179
|
-
<li><span class='ai-risk-label risk-high'>Alt Risc</span> - Probable reincidència</li>
|
|
180
|
+
<li><span class='ai-risk-label risk-high'>Alt Risc</span> - Probable reincidència (de cometre nous delictes)</li>
|
|
180
181
|
<li><span class='ai-risk-label risk-medium'>Risc Mitjà</span> - Probabilitat moderada de reincidència</li>
|
|
181
182
|
<li><span class='ai-risk-label risk-low'>Baix Risc</span> - Improbable reincidència</li>
|
|
182
183
|
</ul>
|
|
183
184
|
|
|
184
185
|
<b>Recorda:</b> Les teves decisions afecten la vida de persones reals i la seguretat pública.
|
|
185
186
|
""",
|
|
186
|
-
"btn_start": "Començar a
|
|
187
|
-
"profiles_title": "👥 Perfils
|
|
188
|
-
"hint_box": "Revisa la informació de cada
|
|
189
|
-
"btn_release": "✓ Alliberar
|
|
190
|
-
"btn_keep": "✗ Mantenir a la
|
|
191
|
-
"btn_show_summary": "📊 Mostrar
|
|
192
|
-
"btn_complete": "Completar aquesta
|
|
193
|
-
"completion_title": "✅ Decisions
|
|
194
|
-
"completion_box_pre": "
|
|
187
|
+
"btn_start": "Començar a prendre decisions ▶️",
|
|
188
|
+
"profiles_title": "👥 Perfils de les persones preses",
|
|
189
|
+
"hint_box": "Revisa la informació de cada persona presa i l'avaluació de riscos de la IA, després pren la teva decisió.",
|
|
190
|
+
"btn_release": "✓ Alliberar",
|
|
191
|
+
"btn_keep": "✗ Mantenir a la presó",
|
|
192
|
+
"btn_show_summary": "📊 Mostrar resum de decisions",
|
|
193
|
+
"btn_complete": "Completar aquesta secció ▶️",
|
|
194
|
+
"completion_title": "✅ Decisions completades!",
|
|
195
|
+
"completion_box_pre": "Ya has pres les teves decisions basant-te en les recomanacions de la IA.<br><br>Ara bé, sorgeix una pregunta clau:<br><br>",
|
|
195
196
|
"completion_question": "I si la IA s'hagués equivocat?",
|
|
196
197
|
"completion_box_post": """
|
|
197
198
|
<p style='font-size:1.1rem;'>
|
|
@@ -203,24 +204,24 @@ TRANSLATIONS = {
|
|
|
203
204
|
Troba la següent secció a sota per continuar el teu viatge.
|
|
204
205
|
</p>
|
|
205
206
|
""",
|
|
206
|
-
"btn_back": "◀️ Tornar a
|
|
207
|
+
"btn_back": "◀️ Tornar a revisar decisions",
|
|
207
208
|
"decision_release": "Alliberar",
|
|
208
|
-
"decision_keep": "Mantenir a la
|
|
209
|
+
"decision_keep": "Mantenir a la presó",
|
|
209
210
|
"decision_recorded": "✓ Decisió registrada:",
|
|
210
|
-
"summary_title": "📊 Resum de les teves
|
|
211
|
-
"summary_released": "
|
|
212
|
-
"summary_kept": "
|
|
211
|
+
"summary_title": "📊 Resum de les teves decisions",
|
|
212
|
+
"summary_released": "Persones preses posades en llibertat:",
|
|
213
|
+
"summary_kept": "Persones preses que continuen a la presó:",
|
|
213
214
|
"summary_empty": "Encara no s'han pres decisions.",
|
|
214
215
|
"nav_loading_profiles": "Carregant perfils...",
|
|
215
216
|
"nav_reviewing": "Revisant les teves decisions...",
|
|
216
|
-
"nav_returning": "
|
|
217
|
-
"label_defendant": "
|
|
217
|
+
"nav_returning": "Tornar a perfils...",
|
|
218
|
+
"label_defendant": "Persona presa",
|
|
218
219
|
"label_age": "Edat",
|
|
219
220
|
"label_gender": "Gènere",
|
|
220
|
-
"label_race": "Raça",
|
|
221
|
-
"label_prior": "Delictes
|
|
222
|
-
"label_charge": "Càrrec
|
|
223
|
-
"label_ai_risk": "🤖 Avaluació de
|
|
221
|
+
"label_race": "Raça/Etnicitat",
|
|
222
|
+
"label_prior": "Delictes previs",
|
|
223
|
+
"label_charge": "Càrrec actual",
|
|
224
|
+
"label_ai_risk": "🤖 Avaluació de risc de la IA:",
|
|
224
225
|
"label_risk": "Risc",
|
|
225
226
|
"label_confidence": "Confiança:",
|
|
226
227
|
"Male": "Masculí",
|
|
@@ -235,7 +236,7 @@ TRANSLATIONS = {
|
|
|
235
236
|
"Theft": "Robatori",
|
|
236
237
|
"Assault": "Assalt",
|
|
237
238
|
"Fraud": "Frau",
|
|
238
|
-
"Burglary": "Robatori amb
|
|
239
|
+
"Burglary": "Robatori amb violació de domicili"
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
242
|
|
|
@@ -312,8 +313,6 @@ def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
|
312
313
|
|
|
313
314
|
profiles = _generate_defendant_profiles()
|
|
314
315
|
|
|
315
|
-
# Track decisions globally for simplicity in this factory context
|
|
316
|
-
decisions = {}
|
|
317
316
|
|
|
318
317
|
# Helpers
|
|
319
318
|
def t(lang, key):
|
|
@@ -356,27 +355,30 @@ def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
|
356
355
|
</div>
|
|
357
356
|
"""
|
|
358
357
|
|
|
359
|
-
def make_decision(defendant_id, decision_type, lang):
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
358
|
+
def make_decision(defendant_id, decision_type, lang, current_decisions):
|
|
359
|
+
"""Record a decision for a defendant safely per user."""
|
|
360
|
+
# Create a copy so we don't affect other users
|
|
361
|
+
new_decisions = current_decisions.copy()
|
|
362
|
+
new_decisions[defendant_id] = decision_type
|
|
363
|
+
|
|
364
|
+
dec_text = t(lang, "decision_release" if decision_type == "Release" else "decision_keep")
|
|
365
|
+
# Return the notification text AND the updated dictionary
|
|
366
|
+
return f"{t(lang, 'decision_recorded')} {dec_text}", new_decisions
|
|
365
367
|
|
|
366
|
-
def get_summary(lang):
|
|
367
|
-
"""Get summary
|
|
368
|
-
if not
|
|
368
|
+
def get_summary(lang, current_decisions):
|
|
369
|
+
"""Get summary based on the specific user's decisions."""
|
|
370
|
+
if not current_decisions:
|
|
369
371
|
return t(lang, "summary_empty")
|
|
370
372
|
|
|
371
|
-
released = sum(1 for d in
|
|
372
|
-
kept = sum(1 for d in
|
|
373
|
+
released = sum(1 for d in current_decisions.values() if d == "Release")
|
|
374
|
+
kept = sum(1 for d in current_decisions.values() if d == "Keep in Prison")
|
|
373
375
|
|
|
374
376
|
summary = f"""
|
|
375
377
|
<div class="summary-box">
|
|
376
378
|
<h3 class="summary-title">{t(lang, 'summary_title')}</h3>
|
|
377
379
|
<div class="summary-body">
|
|
378
|
-
<p><b>{t(lang, 'summary_released')}</b> {released} of {len(
|
|
379
|
-
<p><b>{t(lang, 'summary_kept')}</b> {kept} of {len(
|
|
380
|
+
<p><b>{t(lang, 'summary_released')}</b> {released} of {len(current_decisions)}</p>
|
|
381
|
+
<p><b>{t(lang, 'summary_kept')}</b> {kept} of {len(current_decisions)}</p>
|
|
380
382
|
</div>
|
|
381
383
|
</div>
|
|
382
384
|
"""
|
|
@@ -586,6 +588,7 @@ def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
|
586
588
|
with gr.Blocks(theme=gr.themes.Soft(primary_hue=theme_primary_hue), css=css) as demo:
|
|
587
589
|
# State to hold current language (defaults to 'en')
|
|
588
590
|
lang_state = gr.State(value="en")
|
|
591
|
+
decisions_state = gr.State(value={})
|
|
589
592
|
|
|
590
593
|
# --- UI COMPONENTS (Stored in variables for update) ---
|
|
591
594
|
|
|
@@ -634,24 +637,31 @@ def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
|
634
637
|
# Create UI for each defendant
|
|
635
638
|
for profile in profiles:
|
|
636
639
|
with gr.Column():
|
|
637
|
-
# Profile Card HTML
|
|
640
|
+
# 1. Profile Card HTML
|
|
638
641
|
p_html = gr.HTML(format_profile(profile, "en"))
|
|
639
642
|
|
|
643
|
+
# 2. Define the status text component FIRST (so buttons can reference it)
|
|
644
|
+
decision_status = gr.Markdown("")
|
|
645
|
+
|
|
646
|
+
# 3. Create Row with Buttons
|
|
640
647
|
with gr.Row():
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
648
|
+
# Define the buttons
|
|
649
|
+
p_rel_btn = gr.Button(t("en", "btn_release"), variant="secondary")
|
|
650
|
+
p_keep_btn = gr.Button(t("en", "btn_keep"), variant="stop")
|
|
651
|
+
|
|
652
|
+
# Wire up buttons
|
|
653
|
+
p_rel_btn.click(
|
|
654
|
+
fn=make_decision,
|
|
655
|
+
inputs=[gr.Number(value=profile["id"], visible=False), gr.State(value="Release"), lang_state, decisions_state],
|
|
656
|
+
outputs=[decision_status, decisions_state],
|
|
645
657
|
)
|
|
646
|
-
p_keep_btn
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
658
|
+
p_keep_btn.click(
|
|
659
|
+
fn=make_decision,
|
|
660
|
+
inputs=[gr.Number(value=profile["id"], visible=False), gr.State(value="Keep in Prison"), lang_state, decisions_state],
|
|
661
|
+
outputs=[decision_status, decisions_state],
|
|
650
662
|
)
|
|
651
663
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
# Store for language updates
|
|
664
|
+
# 4. Store elements for language updates
|
|
655
665
|
profile_ui_elements.append({
|
|
656
666
|
"id": profile["id"],
|
|
657
667
|
"profile_data": profile,
|
|
@@ -660,25 +670,17 @@ def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
|
660
670
|
"btn_keep": p_keep_btn
|
|
661
671
|
})
|
|
662
672
|
|
|
663
|
-
# Wire up buttons (passing lang_state)
|
|
664
|
-
p_rel_btn.click(
|
|
665
|
-
lambda l, p_id=profile["id"]: make_decision(p_id, "Release", l),
|
|
666
|
-
inputs=[lang_state],
|
|
667
|
-
outputs=decision_status,
|
|
668
|
-
)
|
|
669
|
-
p_keep_btn.click(
|
|
670
|
-
lambda l, p_id=profile["id"]: make_decision(p_id, "Keep in Prison", l),
|
|
671
|
-
inputs=[lang_state],
|
|
672
|
-
outputs=decision_status,
|
|
673
|
-
)
|
|
674
|
-
|
|
675
673
|
gr.HTML("<hr style='margin:24px 0;'>")
|
|
676
674
|
|
|
677
675
|
# Summary section
|
|
678
676
|
summary_display = gr.HTML("")
|
|
679
677
|
show_summary_btn = gr.Button(t('en', 'btn_show_summary'), variant="primary", size="lg")
|
|
680
678
|
|
|
681
|
-
show_summary_btn.click(
|
|
679
|
+
show_summary_btn.click(
|
|
680
|
+
get_summary,
|
|
681
|
+
inputs=[lang_state, decisions_state], # Pass the state
|
|
682
|
+
outputs=summary_display
|
|
683
|
+
)
|
|
682
684
|
|
|
683
685
|
gr.HTML("<br>")
|
|
684
686
|
complete_btn = gr.Button(t('en', 'btn_complete'), variant="primary", size="lg")
|
|
@@ -701,11 +703,10 @@ def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
|
701
703
|
back_to_profiles_btn = gr.Button(t('en', 'btn_back'))
|
|
702
704
|
|
|
703
705
|
# -------------------------------------------------------------------------
|
|
704
|
-
# I18N UPDATE LOGIC
|
|
706
|
+
# I18N UPDATE LOGIC (CACHED)
|
|
705
707
|
# -------------------------------------------------------------------------
|
|
706
708
|
|
|
707
|
-
#
|
|
708
|
-
# Order matters! Must match the return of update_language
|
|
709
|
+
# 1. Define targets (This remains the same)
|
|
709
710
|
update_targets = [
|
|
710
711
|
lang_state, # 0
|
|
711
712
|
c_main_title, # 1
|
|
@@ -722,56 +723,34 @@ def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
|
722
723
|
back_to_profiles_btn # 12
|
|
723
724
|
]
|
|
724
725
|
|
|
725
|
-
# Add dynamic profile components
|
|
726
|
+
# Add dynamic profile components to targets
|
|
726
727
|
for p_ui in profile_ui_elements:
|
|
727
728
|
update_targets.append(p_ui["html"])
|
|
728
729
|
update_targets.append(p_ui["btn_rel"])
|
|
729
730
|
update_targets.append(p_ui["btn_keep"])
|
|
730
731
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
# Prepare updates
|
|
732
|
+
# 2. Define the Cached Generator
|
|
733
|
+
@lru_cache(maxsize=16)
|
|
734
|
+
def get_cached_ui_updates(lang):
|
|
735
|
+
"""
|
|
736
|
+
Calculates the massive list of HTML strings and Labels ONCE per language.
|
|
737
|
+
"""
|
|
739
738
|
updates = []
|
|
740
739
|
|
|
741
740
|
# 0. State
|
|
742
741
|
updates.append(lang)
|
|
743
742
|
|
|
744
|
-
#
|
|
743
|
+
# Static Elements
|
|
745
744
|
updates.append(f"<h1 style='text-align:center;'>{t(lang, 'title')}</h1>")
|
|
746
|
-
|
|
747
|
-
# 2. Intro
|
|
748
745
|
updates.append(f"""<div class="judge-intro-box">{t(lang, 'intro_role')}</div>""")
|
|
749
|
-
|
|
750
|
-
# 3. Loading
|
|
751
746
|
updates.append(f"""<div style='text-align:center; padding: 100px 0;'><h2 class='loading-title'>{t(lang, 'loading')}</h2></div>""")
|
|
752
|
-
|
|
753
|
-
# 4. Scenario Title
|
|
754
747
|
updates.append(f"<h2 style='text-align:center;'>{t(lang, 'scenario_title')}</h2>")
|
|
755
|
-
|
|
756
|
-
# 5. Scenario Box
|
|
757
748
|
updates.append(f"""<div class="scenario-box">{t(lang, 'scenario_box')}</div>""")
|
|
758
|
-
|
|
759
|
-
# 6. Start Button
|
|
760
749
|
updates.append(gr.Button(value=t(lang, 'btn_start')))
|
|
761
|
-
|
|
762
|
-
# 7. Profiles Title
|
|
763
750
|
updates.append(f"<h2 style='text-align:center;'>{t(lang, 'profiles_title')}</h2>")
|
|
764
|
-
|
|
765
|
-
# 8. Hint Box
|
|
766
751
|
updates.append(f"""<div class="hint-box">{t(lang, 'hint_box')}</div>""")
|
|
767
|
-
|
|
768
|
-
# 9. Summary Button
|
|
769
752
|
updates.append(gr.Button(value=t(lang, 'btn_show_summary')))
|
|
770
|
-
|
|
771
|
-
# 10. Complete Button
|
|
772
753
|
updates.append(gr.Button(value=t(lang, 'btn_complete')))
|
|
773
|
-
|
|
774
|
-
# 11. Completion HTML
|
|
775
754
|
updates.append(f"""
|
|
776
755
|
<div style='text-align:center;'>
|
|
777
756
|
<h2 style='font-size: 2.5rem;'>{t(lang, 'completion_title')}</h2>
|
|
@@ -782,20 +761,27 @@ def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
|
782
761
|
</div>
|
|
783
762
|
</div>
|
|
784
763
|
""")
|
|
785
|
-
|
|
786
|
-
# 12. Back Button
|
|
787
764
|
updates.append(gr.Button(value=t(lang, 'btn_back')))
|
|
788
765
|
|
|
789
|
-
# Dynamic Profiles
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
updates.append(format_profile(
|
|
793
|
-
# Update Buttons
|
|
766
|
+
# Dynamic Profiles (Loop through the data, not the UI elements, to be safe)
|
|
767
|
+
# Note: We rely on 'profiles' being available in this scope.
|
|
768
|
+
for profile in profiles:
|
|
769
|
+
updates.append(format_profile(profile, lang))
|
|
794
770
|
updates.append(gr.Button(value=t(lang, 'btn_release')))
|
|
795
771
|
updates.append(gr.Button(value=t(lang, 'btn_keep')))
|
|
796
772
|
|
|
797
773
|
return updates
|
|
798
774
|
|
|
775
|
+
# 3. Define the Request Handler
|
|
776
|
+
def update_language(request: gr.Request):
|
|
777
|
+
params = request.query_params
|
|
778
|
+
lang = params.get("lang", "en")
|
|
779
|
+
if lang not in TRANSLATIONS:
|
|
780
|
+
lang = "en"
|
|
781
|
+
|
|
782
|
+
# Instant return from cache
|
|
783
|
+
return get_cached_ui_updates(lang)
|
|
784
|
+
|
|
799
785
|
# Trigger update on page load
|
|
800
786
|
demo.load(update_language, inputs=None, outputs=update_targets)
|
|
801
787
|
|
|
@@ -886,3 +872,4 @@ def launch_judge_app(height: int = 1200, share: bool = False, debug: bool = Fals
|
|
|
886
872
|
if __name__ == "__main__":
|
|
887
873
|
launch_judge_app()
|
|
888
874
|
|
|
875
|
+
|