Spaces:
Sleeping
Sleeping
Update app.py
#1
by LbejchJakub - opened
app.py
CHANGED
|
@@ -3,6 +3,11 @@ import json
|
|
| 3 |
import csv
|
| 4 |
from datetime import datetime
|
| 5 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
# Data úložiště
|
| 8 |
RESULTS_FILE = "vysledky_testu.csv"
|
|
@@ -19,7 +24,11 @@ if not os.path.exists(FEEDBACK_FILE):
|
|
| 19 |
writer = csv.writer(f)
|
| 20 |
writer.writerow(['timestamp', 'kod_ucastnika', 'typ_testu', 'obtiznost', 'srozumitelnost', 'uzitecnost', 'komentar'])
|
| 21 |
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
TESTS = {
|
| 24 |
"STAFF": {
|
| 25 |
"nazev": "Test pro administrativní pracovníky (STAFF)",
|
|
@@ -141,7 +150,6 @@ TESTS = {
|
|
| 141 |
"nazev": "Test pro akademické pracovníky (ACADEMIC)",
|
| 142 |
"popis": "12 otázek: 6 základních + 6 metodologických",
|
| 143 |
"otazky": [
|
| 144 |
-
# Základní 1-6
|
| 145 |
{
|
| 146 |
"q": "Jaká je struktura akademického promptu?",
|
| 147 |
"opts": [
|
|
@@ -214,7 +222,6 @@ TESTS = {
|
|
| 214 |
"spravne": ["A", "C", "D"],
|
| 215 |
"body": 1
|
| 216 |
},
|
| 217 |
-
# Metodologické 7-12 (po 1.5 bodu)
|
| 218 |
{
|
| 219 |
"q": "Které použití AI je přijatelné a které ne?",
|
| 220 |
"opts": [
|
|
@@ -337,12 +344,33 @@ def ulozit_feedback(kod, typ, obtiznost, srozumitelnost, uzitecnost, komentar):
|
|
| 337 |
komentar
|
| 338 |
])
|
| 339 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
# Gradio UI
|
| 341 |
-
with gr.Blocks(title="Test AI znalostí KTF UK"
|
| 342 |
gr.Markdown("# 🎓 Test znalostí: Generativní AI")
|
| 343 |
gr.Markdown("**Katolická teologická fakulta UK, Praha 2025**")
|
| 344 |
|
| 345 |
-
# State
|
| 346 |
user_kod = gr.State("")
|
| 347 |
user_typ = gr.State("")
|
| 348 |
user_odpovedi = gr.State({})
|
|
@@ -373,7 +401,7 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 373 |
test_info = gr.Markdown("")
|
| 374 |
|
| 375 |
otazky_ui = []
|
| 376 |
-
for i in range(12):
|
| 377 |
with gr.Group(visible=False) as q_group:
|
| 378 |
q_text = gr.Markdown("")
|
| 379 |
q_check = gr.CheckboxGroup(label="Vyberte VŠECHNY správné odpovědi")
|
|
@@ -385,7 +413,7 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 385 |
result_md = gr.Markdown("")
|
| 386 |
|
| 387 |
gr.Markdown("### 💬 Zpětná vazba")
|
| 388 |
-
gr.Markdown("Pomozte
|
| 389 |
|
| 390 |
obtiznost = gr.Radio(
|
| 391 |
choices=["Příliš snadný", "Přiměřený", "Příliš těžký"],
|
|
@@ -393,14 +421,14 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 393 |
)
|
| 394 |
srozumitelnost = gr.Radio(
|
| 395 |
choices=["Výborná", "Dobrá", "Špatná"],
|
| 396 |
-
label="Srozumitelnost
|
| 397 |
)
|
| 398 |
uzitecnost = gr.Radio(
|
| 399 |
choices=["Velmi užitečné", "Užitečné", "Málo užitečné"],
|
| 400 |
-
label="Užitečnost pro praxi"
|
| 401 |
)
|
| 402 |
komentar = gr.Textbox(
|
| 403 |
-
label="Váš komentář (nepovinné)",
|
| 404 |
placeholder="Sdílejte své postřehy...",
|
| 405 |
lines=3
|
| 406 |
)
|
|
@@ -408,6 +436,28 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 408 |
feedback_btn = gr.Button("Odeslat zpětnou vazbu", variant="secondary")
|
| 409 |
feedback_msg = gr.Markdown("")
|
| 410 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
# Logika
|
| 412 |
def start_test(kod, typ):
|
| 413 |
if not kod or not typ:
|
|
@@ -420,7 +470,6 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 420 |
test = TESTS[typ]
|
| 421 |
num_q = len(test["otazky"])
|
| 422 |
|
| 423 |
-
# Připrav UI pro otázky
|
| 424 |
updates = {
|
| 425 |
start_msg: f"✅ Test zahájen! Přejděte na záložku 'Test'",
|
| 426 |
user_kod: kod,
|
|
@@ -428,7 +477,6 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 428 |
test_info: f"## {test['nazev']}\n\n{test['popis']}\n\n**Počet otázek:** {num_q}"
|
| 429 |
}
|
| 430 |
|
| 431 |
-
# Zobraz otázky
|
| 432 |
for i in range(12):
|
| 433 |
if i < num_q:
|
| 434 |
q = test["otazky"][i]
|
|
@@ -446,25 +494,21 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 446 |
if not kod or not typ:
|
| 447 |
return "❌ Chyba: Test nebyl správně zahájen"
|
| 448 |
|
| 449 |
-
# Sesbírej odpovědi
|
| 450 |
test = TESTS[typ]
|
| 451 |
odpovedi = {}
|
| 452 |
for i in range(len(test["otazky"])):
|
| 453 |
odpovedi[f"q_{i}"] = args[i] if i < len(args) else []
|
| 454 |
|
| 455 |
-
# Vyhodnoť
|
| 456 |
skore, max_skore = vyhodnotit_test(typ, odpovedi)
|
| 457 |
procenta = (skore / max_skore * 100)
|
| 458 |
|
| 459 |
-
# Ulož
|
| 460 |
ulozit_vysledek(kod, typ, skore, max_skore, odpovedi)
|
| 461 |
|
| 462 |
-
# Výsledek
|
| 463 |
if typ == "STAFF":
|
| 464 |
uspech = skore >= 7
|
| 465 |
pozadavek = "min. 7 bodů"
|
| 466 |
else:
|
| 467 |
-
uspech = skore >= 8
|
| 468 |
pozadavek = "min. 8 bodů"
|
| 469 |
|
| 470 |
result = f"""
|
|
@@ -474,7 +518,7 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 474 |
|
| 475 |
**Požadavek:** {pozadavek}
|
| 476 |
|
| 477 |
-
{'🎉 Gratulujeme! Test jste úspěšně absolvovali.' if uspech else '😔 Bohužel jste nesplnili minimální požadavek.
|
| 478 |
|
| 479 |
---
|
| 480 |
|
|
@@ -511,6 +555,18 @@ with gr.Blocks(title="Test AI znalostí KTF UK", theme=gr.themes.Soft()) as demo
|
|
| 511 |
inputs=[user_kod, user_typ, obtiznost, srozumitelnost, uzitecnost, komentar],
|
| 512 |
outputs=[feedback_msg]
|
| 513 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
|
| 515 |
if __name__ == "__main__":
|
| 516 |
demo.launch(share=True)
|
|
|
|
| 3 |
import csv
|
| 4 |
from datetime import datetime
|
| 5 |
import os
|
| 6 |
+
import hashlib
|
| 7 |
+
|
| 8 |
+
# ADMIN HESLO - Změňte toto!
|
| 9 |
+
ADMIN_PASSWORD_HASH = "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8" # "password" - ZMĚŇTE!
|
| 10 |
+
# Pro vytvoření vlastního hashe: import hashlib; hashlib.sha256("vase_heslo".encode()).hexdigest()
|
| 11 |
|
| 12 |
# Data úložiště
|
| 13 |
RESULTS_FILE = "vysledky_testu.csv"
|
|
|
|
| 24 |
writer = csv.writer(f)
|
| 25 |
writer.writerow(['timestamp', 'kod_ucastnika', 'typ_testu', 'obtiznost', 'srozumitelnost', 'uzitecnost', 'komentar'])
|
| 26 |
|
| 27 |
+
def check_admin_password(password):
|
| 28 |
+
"""Ověří admin heslo"""
|
| 29 |
+
return hashlib.sha256(password.encode()).hexdigest() == ADMIN_PASSWORD_HASH
|
| 30 |
+
|
| 31 |
+
# Definice testů (bez správných odpovědí viditelných v UI)
|
| 32 |
TESTS = {
|
| 33 |
"STAFF": {
|
| 34 |
"nazev": "Test pro administrativní pracovníky (STAFF)",
|
|
|
|
| 150 |
"nazev": "Test pro akademické pracovníky (ACADEMIC)",
|
| 151 |
"popis": "12 otázek: 6 základních + 6 metodologických",
|
| 152 |
"otazky": [
|
|
|
|
| 153 |
{
|
| 154 |
"q": "Jaká je struktura akademického promptu?",
|
| 155 |
"opts": [
|
|
|
|
| 222 |
"spravne": ["A", "C", "D"],
|
| 223 |
"body": 1
|
| 224 |
},
|
|
|
|
| 225 |
{
|
| 226 |
"q": "Které použití AI je přijatelné a které ne?",
|
| 227 |
"opts": [
|
|
|
|
| 344 |
komentar
|
| 345 |
])
|
| 346 |
|
| 347 |
+
def get_results_csv(password):
|
| 348 |
+
"""Vrátí CSV s výsledky - pouze pro admina"""
|
| 349 |
+
if not check_admin_password(password):
|
| 350 |
+
return "❌ Nesprávné heslo"
|
| 351 |
+
|
| 352 |
+
if not os.path.exists(RESULTS_FILE):
|
| 353 |
+
return "📭 Zatím žádné výsledky"
|
| 354 |
+
|
| 355 |
+
with open(RESULTS_FILE, 'r', encoding='utf-8') as f:
|
| 356 |
+
return f.read()
|
| 357 |
+
|
| 358 |
+
def get_feedback_csv(password):
|
| 359 |
+
"""Vrátí CSV se zpětnou vazbou - pouze pro admina"""
|
| 360 |
+
if not check_admin_password(password):
|
| 361 |
+
return "❌ Nesprávné heslo"
|
| 362 |
+
|
| 363 |
+
if not os.path.exists(FEEDBACK_FILE):
|
| 364 |
+
return "📭 Zatím žádná zpětná vazba"
|
| 365 |
+
|
| 366 |
+
with open(FEEDBACK_FILE, 'r', encoding='utf-8') as f:
|
| 367 |
+
return f.read()
|
| 368 |
+
|
| 369 |
# Gradio UI
|
| 370 |
+
with gr.Blocks(title="Test AI znalostí KTF UK") as demo:
|
| 371 |
gr.Markdown("# 🎓 Test znalostí: Generativní AI")
|
| 372 |
gr.Markdown("**Katolická teologická fakulta UK, Praha 2025**")
|
| 373 |
|
|
|
|
| 374 |
user_kod = gr.State("")
|
| 375 |
user_typ = gr.State("")
|
| 376 |
user_odpovedi = gr.State({})
|
|
|
|
| 401 |
test_info = gr.Markdown("")
|
| 402 |
|
| 403 |
otazky_ui = []
|
| 404 |
+
for i in range(12):
|
| 405 |
with gr.Group(visible=False) as q_group:
|
| 406 |
q_text = gr.Markdown("")
|
| 407 |
q_check = gr.CheckboxGroup(label="Vyberte VŠECHNY správné odpovědi")
|
|
|
|
| 413 |
result_md = gr.Markdown("")
|
| 414 |
|
| 415 |
gr.Markdown("### 💬 Zpětná vazba")
|
| 416 |
+
gr.Markdown("Pomozte mi prosím zlepšit kurz:")
|
| 417 |
|
| 418 |
obtiznost = gr.Radio(
|
| 419 |
choices=["Příliš snadný", "Přiměřený", "Příliš těžký"],
|
|
|
|
| 421 |
)
|
| 422 |
srozumitelnost = gr.Radio(
|
| 423 |
choices=["Výborná", "Dobrá", "Špatná"],
|
| 424 |
+
label="Srozumitelnost prezentací"
|
| 425 |
)
|
| 426 |
uzitecnost = gr.Radio(
|
| 427 |
choices=["Velmi užitečné", "Užitečné", "Málo užitečné"],
|
| 428 |
+
label="Užitečnost krzu pro praxi"
|
| 429 |
)
|
| 430 |
komentar = gr.Textbox(
|
| 431 |
+
label="Váš komentář ke kurzu (nepovinné)",
|
| 432 |
placeholder="Sdílejte své postřehy...",
|
| 433 |
lines=3
|
| 434 |
)
|
|
|
|
| 436 |
feedback_btn = gr.Button("Odeslat zpětnou vazbu", variant="secondary")
|
| 437 |
feedback_msg = gr.Markdown("")
|
| 438 |
|
| 439 |
+
with gr.Tab("👨💼 Admin") as admin_tab:
|
| 440 |
+
gr.Markdown("### 🔒 Administrátorský přístup")
|
| 441 |
+
gr.Markdown("Pouze pro organizátory testu")
|
| 442 |
+
|
| 443 |
+
admin_password = gr.Textbox(
|
| 444 |
+
label="Admin heslo",
|
| 445 |
+
type="password",
|
| 446 |
+
placeholder="Zadejte heslo"
|
| 447 |
+
)
|
| 448 |
+
|
| 449 |
+
with gr.Row():
|
| 450 |
+
results_btn = gr.Button("Stáhnout výsledky", variant="secondary")
|
| 451 |
+
feedback_download_btn = gr.Button("Stáhnout zpětnou vazbu", variant="secondary")
|
| 452 |
+
|
| 453 |
+
admin_output = gr.Textbox(
|
| 454 |
+
label="Data",
|
| 455 |
+
lines=20,
|
| 456 |
+
max_lines=30
|
| 457 |
+
)
|
| 458 |
+
|
| 459 |
+
gr.Markdown("**ℹ️ Poznámka:** Data můžete zkopírovat a uložit jako CSV soubor.")
|
| 460 |
+
|
| 461 |
# Logika
|
| 462 |
def start_test(kod, typ):
|
| 463 |
if not kod or not typ:
|
|
|
|
| 470 |
test = TESTS[typ]
|
| 471 |
num_q = len(test["otazky"])
|
| 472 |
|
|
|
|
| 473 |
updates = {
|
| 474 |
start_msg: f"✅ Test zahájen! Přejděte na záložku 'Test'",
|
| 475 |
user_kod: kod,
|
|
|
|
| 477 |
test_info: f"## {test['nazev']}\n\n{test['popis']}\n\n**Počet otázek:** {num_q}"
|
| 478 |
}
|
| 479 |
|
|
|
|
| 480 |
for i in range(12):
|
| 481 |
if i < num_q:
|
| 482 |
q = test["otazky"][i]
|
|
|
|
| 494 |
if not kod or not typ:
|
| 495 |
return "❌ Chyba: Test nebyl správně zahájen"
|
| 496 |
|
|
|
|
| 497 |
test = TESTS[typ]
|
| 498 |
odpovedi = {}
|
| 499 |
for i in range(len(test["otazky"])):
|
| 500 |
odpovedi[f"q_{i}"] = args[i] if i < len(args) else []
|
| 501 |
|
|
|
|
| 502 |
skore, max_skore = vyhodnotit_test(typ, odpovedi)
|
| 503 |
procenta = (skore / max_skore * 100)
|
| 504 |
|
|
|
|
| 505 |
ulozit_vysledek(kod, typ, skore, max_skore, odpovedi)
|
| 506 |
|
|
|
|
| 507 |
if typ == "STAFF":
|
| 508 |
uspech = skore >= 7
|
| 509 |
pozadavek = "min. 7 bodů"
|
| 510 |
else:
|
| 511 |
+
uspech = skore >= 8
|
| 512 |
pozadavek = "min. 8 bodů"
|
| 513 |
|
| 514 |
result = f"""
|
|
|
|
| 518 |
|
| 519 |
**Požadavek:** {pozadavek}
|
| 520 |
|
| 521 |
+
{'🎉 Gratulujeme! Test jste úspěšně absolvovali.' if uspech else '😔 Bohužel jste nesplnili minimální požadavek.'}
|
| 522 |
|
| 523 |
---
|
| 524 |
|
|
|
|
| 555 |
inputs=[user_kod, user_typ, obtiznost, srozumitelnost, uzitecnost, komentar],
|
| 556 |
outputs=[feedback_msg]
|
| 557 |
)
|
| 558 |
+
|
| 559 |
+
results_btn.click(
|
| 560 |
+
fn=get_results_csv,
|
| 561 |
+
inputs=[admin_password],
|
| 562 |
+
outputs=[admin_output]
|
| 563 |
+
)
|
| 564 |
+
|
| 565 |
+
feedback_download_btn.click(
|
| 566 |
+
fn=get_feedback_csv,
|
| 567 |
+
inputs=[admin_password],
|
| 568 |
+
outputs=[admin_output]
|
| 569 |
+
)
|
| 570 |
|
| 571 |
if __name__ == "__main__":
|
| 572 |
demo.launch(share=True)
|