Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Extended Big Five questionnaire with subtraits and their questions
|
2 |
+
questionnaire = {
|
3 |
+
"openness": {
|
4 |
+
"imagination": [
|
5 |
+
"Me gusta soñar despierto o pensar en ideas abstractas y fantásticas.",
|
6 |
+
"Al resolver problemas, a menudo se me ocurren soluciones creativas o poco convencionales."
|
7 |
+
],
|
8 |
+
"aesthetic_sensitivity": [
|
9 |
+
"Me conmueve profundamente el arte, la música o la naturaleza.",
|
10 |
+
"A menudo busco la belleza en mi entorno, como disfrutar de las puestas de sol o de espacios bien diseñados."
|
11 |
+
],
|
12 |
+
"intellectual_curiosity": [
|
13 |
+
"Me gusta aprender sobre nuevos temas solo por el placer de aprender.",
|
14 |
+
"Me atraen las ideas complejas o teóricas, como la filosofía o la mecánica cuántica."
|
15 |
+
],
|
16 |
+
"adventure_seeking": [
|
17 |
+
"Prefiero probar nuevas actividades, como viajar a lugares desconocidos o probar comidas exóticas.",
|
18 |
+
"Me siento cómodo tomando riesgos para explorar nuevas oportunidades."
|
19 |
+
],
|
20 |
+
"emotional_openness": [
|
21 |
+
"Estoy dispuesto a expresar mis sentimientos más profundos, incluso si son complicados o vulnerables.",
|
22 |
+
"A menudo reflexiono sobre cómo mis emociones dan forma a mis experiencias."
|
23 |
+
],
|
24 |
+
},
|
25 |
+
"conscientiousness": {
|
26 |
+
"self_discipline": [
|
27 |
+
"Puedo persistir con las tareas incluso cuando se vuelven aburridas o difíciles.",
|
28 |
+
"A menudo completo proyectos antes de las fechas límite."
|
29 |
+
],
|
30 |
+
"orderliness": [
|
31 |
+
"Me gusta mantener mi espacio de trabajo, mi hogar o mi horario bien organizado.",
|
32 |
+
"Me siento incómodo en entornos desordenados o caóticos."
|
33 |
+
],
|
34 |
+
"dutifulness": [
|
35 |
+
"Siento una fuerte obligación de cumplir con mis compromisos, incluso cuando es inconveniente.",
|
36 |
+
"A menudo me siento culpable si no cumplo con las expectativas de los demás."
|
37 |
+
],
|
38 |
+
"achievement_striving": [
|
39 |
+
"Me fijo metas ambiciosas y trabajo duro para alcanzarlas.",
|
40 |
+
"Disfruto de sentirme productivo y realizado después de un día ajetreado."
|
41 |
+
],
|
42 |
+
"cautiousness": [
|
43 |
+
"Me tomo el tiempo de sopesar los pros y los contras antes de tomar decisiones.",
|
44 |
+
"Tengo cuidado de evitar riesgos, incluso si pueden dar lugar a grandes recompensas."
|
45 |
+
],
|
46 |
+
},
|
47 |
+
"extraversion": {
|
48 |
+
"sociability": [
|
49 |
+
"Me siento con energía después de pasar tiempo con otras personas.",
|
50 |
+
"Disfruto de las reuniones grandes y de conocer gente nueva."
|
51 |
+
],
|
52 |
+
"assertiveness": [
|
53 |
+
"Tengo confianza para expresar mis opiniones, incluso en entornos grupales.",
|
54 |
+
"A menudo tomo la iniciativa en la organización de eventos o actividades."
|
55 |
+
],
|
56 |
+
"energy_level": [
|
57 |
+
"Tengo mucha energía física y mental durante todo el día.",
|
58 |
+
"Disfruto de los entornos de ritmo rápido con estimulación constante."
|
59 |
+
],
|
60 |
+
"excitement_seeking": [
|
61 |
+
"Me atraen las actividades emocionantes, como las montañas rusas, el paracaidismo o los viajes de aventura.",
|
62 |
+
"Me aburro rápidamente en entornos rutinarios o de baja energía."
|
63 |
+
],
|
64 |
+
"positive_emotions": [
|
65 |
+
"A menudo me siento alegre, entusiasta y optimista.",
|
66 |
+
"Soy bueno para levantar el ánimo de las personas que me rodean."
|
67 |
+
],
|
68 |
+
},
|
69 |
+
"agreeableness": {
|
70 |
+
"trust": [
|
71 |
+
"Creo que la mayoría de las personas tienen buenas intenciones.",
|
72 |
+
"Me siento cómodo confiando en que los demás hagan su parte en un proyecto grupal."
|
73 |
+
],
|
74 |
+
"altruism": [
|
75 |
+
"Disfruto ayudando a los demás, incluso si requiere un esfuerzo o sacrificio adicional.",
|
76 |
+
"Encuentro satisfacción en el voluntariado o en apoyar una causa."
|
77 |
+
],
|
78 |
+
"modesty": [
|
79 |
+
"Me siento incómodo alardeando de mis logros o habilidades.",
|
80 |
+
"Evito llamar la atención sobre mí mismo, incluso cuando merezco reconocimiento."
|
81 |
+
],
|
82 |
+
"compassion": [
|
83 |
+
"Me doy cuenta rápidamente cuando los demás están molestos o necesitan consuelo.",
|
84 |
+
"Hago todo lo posible para que los demás se sientan cuidados y apoyados."
|
85 |
+
],
|
86 |
+
"cooperation": [
|
87 |
+
"Estoy dispuesto a hacer concesiones para evitar conflictos.",
|
88 |
+
"Priorizo la armonía del grupo por encima de mis propias preferencias en entornos de equipo."
|
89 |
+
],
|
90 |
+
},
|
91 |
+
"neuroticism": {
|
92 |
+
"anxiety": [
|
93 |
+
"A menudo me preocupan los acontecimientos futuros o los posibles problemas.",
|
94 |
+
"Me siento tenso o nervioso en situaciones desconocidas o de mucha presión."
|
95 |
+
],
|
96 |
+
"anger": [
|
97 |
+
"Me siento frustrado o irritado fácilmente.",
|
98 |
+
"A veces, las pequeñas molestias me hacen perder los estribos."
|
99 |
+
],
|
100 |
+
"depression": [
|
101 |
+
"A menudo me siento triste, desanimado o desmotivado, incluso cuando no hay una razón clara.",
|
102 |
+
"Me resulta difícil disfrutar de actividades que antes me hacían feliz."
|
103 |
+
],
|
104 |
+
"self_consciousness": [
|
105 |
+
"Me preocupa demasiado lo que los demás piensen de mí.",
|
106 |
+
"A menudo me siento avergonzado o juzgado en situaciones sociales."
|
107 |
+
],
|
108 |
+
"vulnerability": [
|
109 |
+
"Me resulta difícil afrontar situaciones estresantes o grandes cambios en la vida",
|
110 |
+
"Me siento abrumado cuando me enfrento a desafíos, incluso si son manejables".
|
111 |
+
],
|
112 |
+
},
|
113 |
+
}
|
114 |
+
|
115 |
+
import json
|
116 |
+
import numpy as np
|
117 |
+
import gradio as gr
|
118 |
+
import plotly.graph_objects as go
|
119 |
+
from scipy.stats import percentileofscore
|
120 |
+
|
121 |
+
# Define TRAIT_COLORS
|
122 |
+
TRAIT_COLORS = {
|
123 |
+
"openness": "blue",
|
124 |
+
"conscientiousness": "green",
|
125 |
+
"extraversion": "orange",
|
126 |
+
"agreeableness": "purple",
|
127 |
+
"neuroticism": "red"
|
128 |
+
}
|
129 |
+
|
130 |
+
# Flatten questions dynamically
|
131 |
+
def build_questions():
|
132 |
+
return [
|
133 |
+
(trait, sub_trait, q)
|
134 |
+
for trait, sub_traits in questionnaire.items()
|
135 |
+
for sub_trait, qs in sub_traits.items()
|
136 |
+
for q in qs
|
137 |
+
]
|
138 |
+
|
139 |
+
questions = build_questions()
|
140 |
+
|
141 |
+
# Initialize state
|
142 |
+
state = {"current_question": 0, "responses": []}
|
143 |
+
|
144 |
+
# Compute scores with percentiles and z-scores
|
145 |
+
def compute_scores_and_percentiles(responses):
|
146 |
+
scores = {}
|
147 |
+
idx = 0
|
148 |
+
for trait, sub_traits in questionnaire.items():
|
149 |
+
for sub_trait, qs in sub_traits.items():
|
150 |
+
mean_score = np.mean(responses[idx:idx + len(qs)])
|
151 |
+
scores[f"{trait}_{sub_trait}"] = mean_score
|
152 |
+
idx += len(qs)
|
153 |
+
|
154 |
+
# Convert scores to arrays for percentile/z-score calculations
|
155 |
+
values = np.array(list(scores.values()))
|
156 |
+
z_scores = (values - np.mean(values)) / np.std(values)
|
157 |
+
percentiles = [percentileofscore(values, score) for score in values]
|
158 |
+
|
159 |
+
return scores, z_scores, percentiles
|
160 |
+
|
161 |
+
# Create chart with colors, percentiles, and z-scores
|
162 |
+
def create_chart(scores, z_scores, percentiles):
|
163 |
+
subtraits = [key.split("_")[1] for key in scores.keys()]
|
164 |
+
values = list(scores.values())
|
165 |
+
trait_keys = [key.split("_")[0] for key in scores.keys()]
|
166 |
+
colors = [TRAIT_COLORS[trait] for trait in trait_keys]
|
167 |
+
|
168 |
+
# Create a bar chart
|
169 |
+
fig = go.Figure()
|
170 |
+
|
171 |
+
for i, (trait, color) in enumerate(TRAIT_COLORS.items()):
|
172 |
+
indices = [j for j, t in enumerate(trait_keys) if t == trait]
|
173 |
+
trait_subtraits = [subtraits[j] for j in indices]
|
174 |
+
trait_values = [values[j] for j in indices]
|
175 |
+
trait_z_scores = [z_scores[j] for j in indices]
|
176 |
+
trait_percentiles = [percentiles[j] for j in indices]
|
177 |
+
|
178 |
+
fig.add_trace(
|
179 |
+
go.Bar(
|
180 |
+
x=trait_subtraits,
|
181 |
+
y=trait_values,
|
182 |
+
name=trait.capitalize(),
|
183 |
+
marker_color=color,
|
184 |
+
text=[
|
185 |
+
f"Score: {v:.2f}<br>Z-score: {z:.2f}<br>Percentile: {p:.1f}%"
|
186 |
+
for v, z, p in zip(trait_values, trait_z_scores, trait_percentiles)
|
187 |
+
],
|
188 |
+
hoverinfo="text"
|
189 |
+
)
|
190 |
+
)
|
191 |
+
|
192 |
+
fig.update_layout(
|
193 |
+
title="Trait Breakdown with Percentiles and Z-Scores",
|
194 |
+
xaxis_title="Subtraits",
|
195 |
+
yaxis_title="Average Score (1-10)",
|
196 |
+
plot_bgcolor="black",
|
197 |
+
paper_bgcolor="black",
|
198 |
+
font=dict(color="white"),
|
199 |
+
legend=dict(
|
200 |
+
title="Traits",
|
201 |
+
bgcolor="black",
|
202 |
+
bordercolor="gray",
|
203 |
+
borderwidth=1
|
204 |
+
)
|
205 |
+
)
|
206 |
+
return fig
|
207 |
+
|
208 |
+
# Progress gauge
|
209 |
+
def plot_progress(current, total, question_text, question_num):
|
210 |
+
progress = (current / total) * 100
|
211 |
+
fig = go.Figure(go.Indicator(
|
212 |
+
mode="gauge+number",
|
213 |
+
value=progress,
|
214 |
+
gauge={
|
215 |
+
'axis': {'range': [0, 100]},
|
216 |
+
'bar': {'color': 'purple'},
|
217 |
+
'bgcolor': 'black',
|
218 |
+
'steps': [
|
219 |
+
{'range': [0, 25], 'color': 'darkviolet'},
|
220 |
+
{'range': [25, 50], 'color': 'violet'},
|
221 |
+
{'range': [50, 75], 'color': 'magenta'},
|
222 |
+
{'range': [75, 100], 'color': 'plum'}
|
223 |
+
],
|
224 |
+
},
|
225 |
+
domain={'x': [0, 1], 'y': [0, 1]}
|
226 |
+
))
|
227 |
+
|
228 |
+
fig.add_annotation(
|
229 |
+
x=0.5, y=-0.2, text=f"Question {question_num} / {total}", showarrow=False,
|
230 |
+
font=dict(size=14, color='white'), align="center"
|
231 |
+
)
|
232 |
+
|
233 |
+
fig.update_layout(
|
234 |
+
title={
|
235 |
+
'text': f"<b>{question_text}</b>",
|
236 |
+
'font': {'size': 20, 'color': "white"},
|
237 |
+
'x': 0.5,
|
238 |
+
'xanchor': 'center',
|
239 |
+
'y': 0.85,
|
240 |
+
},
|
241 |
+
margin=dict(t=170),
|
242 |
+
plot_bgcolor='black',
|
243 |
+
paper_bgcolor='black',
|
244 |
+
font=dict(color='white'),
|
245 |
+
)
|
246 |
+
|
247 |
+
return fig
|
248 |
+
|
249 |
+
# Start test
|
250 |
+
def start_test():
|
251 |
+
state["current_question"] = 0
|
252 |
+
state["responses"] = []
|
253 |
+
question = questions[state["current_question"]][2]
|
254 |
+
return (
|
255 |
+
question,
|
256 |
+
plot_progress(0, len(questions), question, 1),
|
257 |
+
gr.update(visible=True),
|
258 |
+
gr.update(visible=False),
|
259 |
+
)
|
260 |
+
|
261 |
+
|
262 |
+
|
263 |
+
|
264 |
+
|
265 |
+
# Save Plotly chart as HTML
|
266 |
+
def save_plotly_html(chart, file_name="results_chart.html"):
|
267 |
+
chart.write_html(file_name)
|
268 |
+
print(f"Chart saved to {file_name}")
|
269 |
+
|
270 |
+
# Save test results as a JSON file
|
271 |
+
def save_results(responses, file_name="test_results.json"):
|
272 |
+
with open(file_name, "w") as f:
|
273 |
+
json.dump({"responses": responses}, f, indent=4)
|
274 |
+
print(f"Results saved to {file_name}")
|
275 |
+
|
276 |
+
# Modified next_question to save results and chart
|
277 |
+
def next_question(response):
|
278 |
+
state["responses"].append(int(response))
|
279 |
+
state["current_question"] += 1
|
280 |
+
|
281 |
+
if state["current_question"] >= len(questions):
|
282 |
+
scores, z_scores, percentiles = compute_scores_and_percentiles(state["responses"])
|
283 |
+
result_chart = create_chart(scores, z_scores, percentiles)
|
284 |
+
|
285 |
+
# Save results and chart
|
286 |
+
save_results(state["responses"], "test_results.json")
|
287 |
+
save_plotly_html(result_chart, "results_chart.html")
|
288 |
+
|
289 |
+
return "Test Complete! Your results:", result_chart, gr.update(visible=False), gr.update(visible=True)
|
290 |
+
|
291 |
+
question = questions[state["current_question"]][2]
|
292 |
+
return (
|
293 |
+
question,
|
294 |
+
plot_progress(
|
295 |
+
state["current_question"], len(questions),
|
296 |
+
question, state["current_question"] + 1
|
297 |
+
),
|
298 |
+
gr.update(visible=True),
|
299 |
+
gr.update(visible=False),
|
300 |
+
)
|
301 |
+
|
302 |
+
def create_gradio_app():
|
303 |
+
with gr.Blocks() as app:
|
304 |
+
gr.Markdown("## Extended Big Five Personality Test")
|
305 |
+
|
306 |
+
# UI Elements
|
307 |
+
start_button = gr.Button("Start Test")
|
308 |
+
question_text = gr.Textbox(label="Question", interactive=False, visible=True)
|
309 |
+
button_group = gr.Radio([str(i) for i in range(1, 11)], label="Your Response (1-10)", visible=True)
|
310 |
+
progress_gauge = gr.Plot()
|
311 |
+
result_output = gr.Textbox(label="Results", visible=True)
|
312 |
+
download_results = gr.File(label="Download Results JSON", visible=False)
|
313 |
+
download_chart = gr.File(label="Download Chart HTML", visible=True)
|
314 |
+
|
315 |
+
# Start Test Logic
|
316 |
+
start_button.click(
|
317 |
+
start_test,
|
318 |
+
outputs=[question_text, progress_gauge, button_group, result_output]
|
319 |
+
)
|
320 |
+
|
321 |
+
# Question Logic
|
322 |
+
def handle_next_question(response):
|
323 |
+
output = next_question(response)
|
324 |
+
|
325 |
+
# Check if the test is complete to enable downloads
|
326 |
+
if state["current_question"] >= len(questions):
|
327 |
+
return (*output, "test_results.json", "results_chart.html")
|
328 |
+
else:
|
329 |
+
return (*output, None, None)
|
330 |
+
|
331 |
+
# Respond to button changes and enable downloads after test completion
|
332 |
+
button_group.change(
|
333 |
+
handle_next_question,
|
334 |
+
inputs=button_group,
|
335 |
+
outputs=[
|
336 |
+
question_text, progress_gauge, button_group, result_output,
|
337 |
+
download_results, download_chart
|
338 |
+
]
|
339 |
+
)
|
340 |
+
|
341 |
+
return app
|
342 |
+
|
343 |
+
|
344 |
+
# Launch the Gradio app
|
345 |
+
app = create_gradio_app()
|
346 |
+
|
347 |
+
# Launch the app locally
|
348 |
+
app.launch()
|