feat: Add lore pack, UI adjustments, requirements.txt and INSTALL.md
This commit is contained in:
+89
@@ -0,0 +1,89 @@
|
|||||||
|
# Magisches Harry Potter Escape Quiz Portal
|
||||||
|
|
||||||
|
Interaktives Harry Potter Escape Quiz Portal (FastAPI & MariaDB).
|
||||||
|
|
||||||
|
## Systemanforderungen
|
||||||
|
- Debian 12 / 13 (oder kompatibles Linux)
|
||||||
|
- Python 3.13+ mit `python3-venv`
|
||||||
|
- Nginx (Reverse Proxy mit SSL)
|
||||||
|
- MariaDB Server auf \`db.waiswuaschd.de:3306\` (Datenbank: \`v2_db_quiz\`)
|
||||||
|
|
||||||
|
## 1. Installation
|
||||||
|
```bash
|
||||||
|
# Repository klonen
|
||||||
|
cd /home/maxi/Projekt
|
||||||
|
git clone https://gitea.sp13l.de/kobold-ai/quiz.git quiz.waiswuaschd.de
|
||||||
|
cd quiz.waiswuaschd.de
|
||||||
|
|
||||||
|
# Python Virtualenv erstellen und Abh?ngigkeiten installieren
|
||||||
|
python3 -m venv venv
|
||||||
|
./venv/bin/pip install --upgrade pip
|
||||||
|
./venv/bin/pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Umgebungsvariablen (.env)
|
||||||
|
Erstelle im Projektverzeichnis eine `.env` Datei:
|
||||||
|
```ini
|
||||||
|
DB_HOST=db.waiswuaschd.de
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_NAME=v2_db_quiz
|
||||||
|
DB_USER=v2_usr_quiz
|
||||||
|
DB_PASS=Quiz_Pass_2026_Secure!
|
||||||
|
SECRET_KEY=hogwarts_magic_super_secret_key_2026_dev_quiz
|
||||||
|
ADMIN_USER=admin
|
||||||
|
ADMIN_PASS=Alohomora2026!
|
||||||
|
PORT=8001
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Systemd Service (/etc/systemd/system/quiz.service)
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Magisches Quiz Portal (FastAPI)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=maxi
|
||||||
|
Group=maxi
|
||||||
|
WorkingDirectory=/home/maxi/Projekt/quiz.waiswuaschd.de
|
||||||
|
ExecStart=/home/maxi/Projekt/quiz.waiswuaschd.de/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8001 --workers 1
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
Aktivieren und starten:
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now quiz
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Nginx Reverse Proxy
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name quiz.dev.waiswuaschd.de;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name quiz.dev.waiswuaschd.de;
|
||||||
|
|
||||||
|
# SSL Zertifikate hier konfigurieren (z. B. via certbot)
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias /home/maxi/Projekt/quiz.waiswuaschd.de/app/static/;
|
||||||
|
add_header Cache-Control "public, max-age=3600, must-revalidate";
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:8001;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
+26
-5
@@ -20,7 +20,7 @@ from app.schemas import GameCheckRequest, SuggestionCreateRequest, GameStartRequ
|
|||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
app = FastAPI(title="Magisches Harry-Potter-Escape-Quiz")
|
app = FastAPI(title="PotterTrivia – Das Harry-Potter-Wissensquiz")
|
||||||
|
|
||||||
SECRET_KEY = os.getenv("SECRET_KEY", "hogwarts_secret_default_key_2026")
|
SECRET_KEY = os.getenv("SECRET_KEY", "hogwarts_secret_default_key_2026")
|
||||||
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY, session_cookie="quiz_session")
|
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY, session_cookie="quiz_session")
|
||||||
@@ -154,16 +154,37 @@ async def index(request: Request, db: Session = Depends(get_db)):
|
|||||||
context={"question_count": active_count}
|
context={"question_count": active_count}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
VALID_CATEGORIES = ["all", "books", "movies", "creatures", "spells", "characters", "hogwarts", "quidditch", "lore", "general"]
|
||||||
|
|
||||||
@app.post("/api/game/start")
|
@app.post("/api/game/start")
|
||||||
async def start_game(payload: GameStartRequest = None, db: Session = Depends(get_db)):
|
async def start_game(payload: GameStartRequest = None, db: Session = Depends(get_db)):
|
||||||
lang = payload.lang if payload and payload.lang in ["de", "en"] else "de"
|
lang = payload.lang if payload and payload.lang in ["de", "en"] else "de"
|
||||||
difficulty = payload.difficulty if payload and payload.difficulty in ["easy", "medium", "hard"] else "all"
|
difficulty = payload.difficulty if payload and payload.difficulty in ["easy", "medium", "hard"] else "all"
|
||||||
|
category = payload.category if payload and payload.category in VALID_CATEGORIES else "all"
|
||||||
|
|
||||||
|
base_query = db.query(Question).filter(Question.status == "active")
|
||||||
|
if category != "all":
|
||||||
|
if category == "books":
|
||||||
|
cat_query = base_query.filter((Question.category == "books") | (Question.question.like("%Kröter%")))
|
||||||
|
else:
|
||||||
|
cat_query = base_query.filter(Question.category == category)
|
||||||
|
else:
|
||||||
|
cat_query = base_query
|
||||||
|
|
||||||
query = db.query(Question).filter(Question.status == "active")
|
|
||||||
if difficulty != "all":
|
if difficulty != "all":
|
||||||
query = query.filter(Question.difficulty == difficulty)
|
strict_pool = cat_query.filter(Question.difficulty == difficulty).all()
|
||||||
|
if len(strict_pool) >= 10 or category == "all":
|
||||||
|
pool = strict_pool
|
||||||
|
else:
|
||||||
|
# Keep all difficulty matches in this category and top up from the same category
|
||||||
|
cat_all = cat_query.all()
|
||||||
|
strict_ids = {q.id for q in strict_pool}
|
||||||
|
extra = [q for q in cat_all if q.id not in strict_ids]
|
||||||
|
random.shuffle(extra)
|
||||||
|
pool = strict_pool + extra[:max(0, 10 - len(strict_pool))]
|
||||||
|
else:
|
||||||
|
pool = cat_query.all()
|
||||||
|
|
||||||
pool = query.all()
|
|
||||||
if not pool:
|
if not pool:
|
||||||
pool = db.query(Question).filter(Question.status == "active").all()
|
pool = db.query(Question).filter(Question.status == "active").all()
|
||||||
if not pool:
|
if not pool:
|
||||||
@@ -538,7 +559,7 @@ async def submit_suggestion(payload: SuggestionCreateRequest, db: Session = Depe
|
|||||||
opts.append(opt.strip())
|
opts.append(opt.strip())
|
||||||
options_json = json.dumps(opts, ensure_ascii=False)
|
options_json = json.dumps(opts, ensure_ascii=False)
|
||||||
|
|
||||||
chosen_cat = payload.category if payload.category in ["hogwarts", "characters", "spells", "creatures", "quidditch", "lore", "general"] else "general"
|
chosen_cat = payload.category if payload.category in ["books", "movies", "hogwarts", "characters", "spells", "creatures", "quidditch", "lore", "general"] else "general"
|
||||||
diff = payload.difficulty if payload.difficulty in ["easy", "medium", "hard"] else "medium"
|
diff = payload.difficulty if payload.difficulty in ["easy", "medium", "hard"] else "medium"
|
||||||
|
|
||||||
new_suggestion = Question(
|
new_suggestion = Question(
|
||||||
|
|||||||
@@ -115,4 +115,22 @@ def is_answer_correct(user_input: str, canonical: str, accepted_raw: str | None
|
|||||||
if dist <= max_allowed:
|
if dist <= max_allowed:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Support compound answers connected by 'und' / 'and' (e.g. "Mantikor und Feuerkrabbe")
|
||||||
|
for sep in (" und ", " and "):
|
||||||
|
if sep in target:
|
||||||
|
parts = [p.strip() for p in target.split(sep) if len(p.strip()) >= 3]
|
||||||
|
if len(parts) == 2:
|
||||||
|
u_words = u.split()
|
||||||
|
matched_both = True
|
||||||
|
for part in parts:
|
||||||
|
if part in u:
|
||||||
|
continue
|
||||||
|
# Check fuzzy match against tokens or reversed order
|
||||||
|
part_matched = any(levenshtein_distance(w, part) <= (1 if len(part) <= 6 else 2) for w in u_words)
|
||||||
|
if not part_matched:
|
||||||
|
matched_both = False
|
||||||
|
break
|
||||||
|
if matched_both:
|
||||||
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from typing import Optional, List
|
|||||||
class GameStartRequest(BaseModel):
|
class GameStartRequest(BaseModel):
|
||||||
lang: Optional[str] = "de"
|
lang: Optional[str] = "de"
|
||||||
difficulty: Optional[str] = "all"
|
difficulty: Optional[str] = "all"
|
||||||
|
category: Optional[str] = "all"
|
||||||
recent_ids: Optional[List[int]] = None
|
recent_ids: Optional[List[int]] = None
|
||||||
wrong_ids: Optional[List[int]] = None
|
wrong_ids: Optional[List[int]] = None
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from app.database import engine, SessionLocal, Base
|
|||||||
from app.models import Question, AdminUser
|
from app.models import Question, AdminUser
|
||||||
from app.auth import hash_password
|
from app.auth import hash_password
|
||||||
from app.seed_40 import seed_40_questions
|
from app.seed_40 import seed_40_questions
|
||||||
|
from app.seed_lore_pack import seed_lore_pack
|
||||||
|
|
||||||
def seed_db():
|
def seed_db():
|
||||||
Base.metadata.create_all(bind=engine)
|
Base.metadata.create_all(bind=engine)
|
||||||
@@ -29,6 +30,9 @@ def seed_db():
|
|||||||
print("Question table is empty, seeding 40 initial bilingual questions...")
|
print("Question table is empty, seeding 40 initial bilingual questions...")
|
||||||
seed_40_questions()
|
seed_40_questions()
|
||||||
|
|
||||||
|
# 3. Ensure Lore Pack (Books, Movies, Hard Creatures incl. Blast-Ended Skrewts) is present
|
||||||
|
seed_lore_pack()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,722 @@
|
|||||||
|
import json
|
||||||
|
from app.database import SessionLocal
|
||||||
|
from app.models import Question
|
||||||
|
|
||||||
|
LORE_PACK_QUESTIONS = [
|
||||||
|
# --- CREATURES (Magische Geschöpfe) ---
|
||||||
|
{
|
||||||
|
"category": "creatures",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Aus welcher Kreuzung zweier magischer Geschöpfe züchtete Hagrid die Knallrümpfigen Kröter?",
|
||||||
|
"canonical_answer": "Mantikor und Feuerkrabbe",
|
||||||
|
"accepted_answers": json.dumps([
|
||||||
|
"Mantikor und Feuerkrabbe",
|
||||||
|
"Feuerkrabbe und Mantikor",
|
||||||
|
"Mantikor Feuerkrabbe",
|
||||||
|
"Feuerkrabbe Mantikor",
|
||||||
|
"Manticore and Fire Crab",
|
||||||
|
"Fire Crab and Manticore"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Eines ist ein hochgefährliches griechisches Ungeheuer mit Skorpionschwanz, das andere ein schildkrötenähnliches Wesen von den Fidschi-Inseln.",
|
||||||
|
"question_en": "Which two magical creatures did Hagrid crossbreed to create the Blast-Ended Skrewts?",
|
||||||
|
"answer_en": "Manticore and Fire Crab",
|
||||||
|
"hint_en": "One is a deadly Greek beast with a scorpion tail, the other is a turtle-like creature native to Fiji."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "creatures",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welcher der drei Köpfe der dreiköpfigen Schlange 'Runespoor' ist laut Newt Scamander derjenige, der Pläne schmiedet?",
|
||||||
|
"canonical_answer": "Der linke Kopf",
|
||||||
|
"options": json.dumps(["Der linke Kopf", "Der mittlere Kopf", "Der rechte Kopf", "Alle drei gleichzeitig"], ensure_ascii=False),
|
||||||
|
"hint": "Der mittlere Kopf ist der Träumer und der rechte Kopf der Kritiker.",
|
||||||
|
"question_en": "According to Newt Scamander, which of the three heads of the Runespoor snake is the planner?",
|
||||||
|
"answer_en": "The left head",
|
||||||
|
"options_en": json.dumps(["The left head", "The middle head", "The right head", "All three simultaneously"], ensure_ascii=False),
|
||||||
|
"hint_en": "The middle head is the dreamer and the right head is the critic."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "creatures",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Wie heißt die riesige Spinne (Acromantula), die Hagrid schon während seiner Schulzeit in einem Schrank in Hogwarts versteckte?",
|
||||||
|
"canonical_answer": "Aragog",
|
||||||
|
"accepted_answers": json.dumps(["Aragog"], ensure_ascii=False),
|
||||||
|
"hint": "Sein Name beginnt mit 'A' und er lebt später mit seiner großen Kolonie im Verbotenen Wald.",
|
||||||
|
"question_en": "What is the name of the giant Acromantula spider that Hagrid hid in a cupboard at Hogwarts during his school days?",
|
||||||
|
"answer_en": "Aragog",
|
||||||
|
"hint_en": "His name starts with 'A' and he later lives with his large colony in the Forbidden Forest."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "creatures",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welche Gefahrenklasse (Ministeriumsklassifizierung) hat ein ausgewachsener Nundu laut 'Phantastische Tierwesen'?",
|
||||||
|
"canonical_answer": "XXXXX (Bekannte Zauberertöter)",
|
||||||
|
"options": json.dumps([
|
||||||
|
"XXXXX (Bekannte Zauberertöter)",
|
||||||
|
"XXXX (Gefährlich / erfordert Fachwissen)",
|
||||||
|
"XXX (Für fähige Zauberer kein Problem)",
|
||||||
|
"XX (Harmlos / kann gezähmt werden)"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Es braucht mindestens hundert zusammenarbeitende Zauberer, um einen einzigen Nundu zu überwältigen.",
|
||||||
|
"question_en": "What Ministry of Magic classification does an adult Nundu hold according to 'Fantastic Beasts'?",
|
||||||
|
"answer_en": "XXXXX (Known wizard killer)",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"XXXXX (Known wizard killer)",
|
||||||
|
"XXXX (Dangerous / requires specialist knowledge)",
|
||||||
|
"XXX (Competent wizard should cope)",
|
||||||
|
"XX (Harmless / may be domesticated)"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "It takes at least a hundred skilled wizards working together to subdue a single Nundu."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "creatures",
|
||||||
|
"difficulty": "easy",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Welches kleine, pelzige Tierwesen wird magisch von allem Glitzernden und von Goldmünzen angezogen?",
|
||||||
|
"canonical_answer": "Niffler",
|
||||||
|
"accepted_answers": json.dumps(["Niffler", "Ein Niffler"], ensure_ascii=False),
|
||||||
|
"hint": "Newt Scamander hat eines dieser langnasigen Beuteltiere in seinem Koffer.",
|
||||||
|
"question_en": "Which small, furry magical creature is irresistibly drawn to anything shiny and gold coins?",
|
||||||
|
"answer_en": "Niffler",
|
||||||
|
"hint_en": "Newt Scamander keeps one of these long-snouted burrowing creatures in his case."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "creatures",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wie nennt man das unsichtbare geflügelte Pferd, das nur von Menschen gesehen werden kann, die den Tod bereits bewusst miterlebt haben?",
|
||||||
|
"canonical_answer": "Thestral",
|
||||||
|
"options": json.dumps(["Thestral", "Hippogreif", "Abraxaner", "Kelpie"], ensure_ascii=False),
|
||||||
|
"hint": "Sie ziehen die Kutschen vom Bahnhof Hogsmeade zum Schloss Hogwarts.",
|
||||||
|
"question_en": "What is the name of the invisible winged horse that can only be seen by those who have witnessed death?",
|
||||||
|
"answer_en": "Thestral",
|
||||||
|
"options_en": json.dumps(["Thestral", "Hippogriff", "Abraxan", "Kelpie"], ensure_ascii=False),
|
||||||
|
"hint_en": "They pull the carriages from Hogsmeade station up to Hogwarts Castle."
|
||||||
|
},
|
||||||
|
|
||||||
|
# --- BOOKS (Buchwissen) ---
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Wie lautet das vollständige Akronym der Organisation, die Hermine Granger im 4. Buch zur Befreiung der Hauselfen gründet?",
|
||||||
|
"canonical_answer": "B.ELFE.R.",
|
||||||
|
"accepted_answers": json.dumps(["B.ELFE.R.", "BELFER", "B.ELFE.R", "S.P.E.W.", "SPEW", "Bund für Elfenrechte"]),
|
||||||
|
"hint": "Es steht für 'Bund für Elfenrechte' (im Englischen: S.P.E.W.).",
|
||||||
|
"question_en": "What is the acronym of the organization Hermione Granger founds in her fourth year to campaign for house-elf rights?",
|
||||||
|
"answer_en": "S.P.E.W.",
|
||||||
|
"hint_en": "It stands for 'Society for the Promotion of Elfish Welfare' (in German: B.ELFE.R.)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wer schenkt Harry im 4. Buch (im Gegensatz zum Film!) das Kraut 'Dianthuskraut' (Gillyweed) für die zweite Aufgabe des Trimagischen Turniers?",
|
||||||
|
"canonical_answer": "Dobby",
|
||||||
|
"options": json.dumps(["Dobby", "Neville Longbottom", "Alastor Moody", "Cedric Diggory"], ensure_ascii=False),
|
||||||
|
"hint": "Er stiehlt es aus Professor Snapes Vorratskammer, nachdem er ein inszeniertes Gespräch von Barty Crouch Jr. belauscht hat.",
|
||||||
|
"question_en": "In the Goblet of Fire book (unlike the movie!), who actually gives Harry the Gillyweed before the Second Task?",
|
||||||
|
"answer_en": "Dobby",
|
||||||
|
"options_en": json.dumps(["Dobby", "Neville Longbottom", "Alastor Moody", "Cedric Diggory"], ensure_ascii=False),
|
||||||
|
"hint_en": "He steals it from Professor Snape's private stores after overhearing a staged conversation by Barty Crouch Jr."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Wie heißt der Poltergeist von Hogwarts, der in allen sieben Büchern für Chaos sorgt, aber aus den Verfilmungen komplett gestrichen wurde?",
|
||||||
|
"canonical_answer": "Peeves",
|
||||||
|
"accepted_answers": json.dumps(["Peeves", "Poltergeist Peeves"]),
|
||||||
|
"hint": "Nur der Blutige Baron hat ihn wirklich unter Kontrolle.",
|
||||||
|
"question_en": "What is the name of the Hogwarts poltergeist who causes chaos throughout all seven books but was completely cut from the films?",
|
||||||
|
"answer_en": "Peeves",
|
||||||
|
"hint_en": "Only the Bloody Baron can truly keep him in line."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welches Rätsel stellt Professor Snape in Band 1 als Schutz vor dem Stein der Weisen auf, das im ersten Kinofilm komplett fehlt?",
|
||||||
|
"canonical_answer": "Ein Logikrätsel mit sieben Zaubertrank-Flaschen",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Ein Logikrätsel mit sieben Zaubertrank-Flaschen",
|
||||||
|
"Ein Duell gegen eine verzauberte Rüstung",
|
||||||
|
"Ein Schachbrett aus flüssigem Quecksilber",
|
||||||
|
"Eine Tür, die nur auf Parsel reagiert"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Hermine löst es mit reiner Logik, während ein schwarzes und ein violettes Feuer die Ausgänge blockieren.",
|
||||||
|
"question_en": "Which obstacle guarding the Philosopher's Stone was created by Professor Snape in the first book, but omitted from the film?",
|
||||||
|
"answer_en": "A logic puzzle with seven potion bottles",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"A logic puzzle with seven potion bottles",
|
||||||
|
"A duel against an enchanted suit of armor",
|
||||||
|
"A chessboard made of liquid mercury",
|
||||||
|
"A door that only opens to Parseltongue"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "Hermione solves it using pure logic while black and purple flames block the exits."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Wie heißt die Hauselfe der Familie Barty Crouch, die beim Quidditch-Weltpokal mit Harrys Zauberstab in der Hand gefunden wird?",
|
||||||
|
"canonical_answer": "Winky",
|
||||||
|
"accepted_answers": json.dumps(["Winky", "Hauselfe Winky"]),
|
||||||
|
"hint": "Ihr Name beginnt mit 'W' und sie verfällt nach ihrer Entlassung dem Butterbier.",
|
||||||
|
"question_en": "What is the name of the Crouch family's female house-elf who is found holding Harry's wand at the Quidditch World Cup?",
|
||||||
|
"answer_en": "Winky",
|
||||||
|
"hint_en": "Her name starts with 'W' and she later develops a Butterbeer habit in the Hogwarts kitchens."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wie heißt Tom Riddles Mutter mit vollem Geburtsnamen, deren tragische Geschichte Harry in Dumbledores Denkarium im 6. Buch erfährt?",
|
||||||
|
"canonical_answer": "Merope Gaunt",
|
||||||
|
"options": json.dumps(["Merope Gaunt", "Morfin Gaunt", "Kendra Dumbledore", "Eileen Prince"], ensure_ascii=False),
|
||||||
|
"hint": "Sie war eine Nachfahrin von Salazar Slytherin und verliebte sich in den Muggel Tom Riddle Senior.",
|
||||||
|
"question_en": "What was the maiden name of Tom Riddle's mother, whose tragic story Harry learns in Dumbledore's Pensieve in book 6?",
|
||||||
|
"answer_en": "Merope Gaunt",
|
||||||
|
"options_en": json.dumps(["Merope Gaunt", "Morfin Gaunt", "Kendra Dumbledore", "Eileen Prince"], ensure_ascii=False),
|
||||||
|
"hint_en": "She was a direct descendant of Salazar Slytherin and fell in love with the Muggle Tom Riddle Sr."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Auf welcher Krankenstation im St.-Mungo-Hospital begegnen Harry, Ron und Hermine im 5. Buch zufällig Gilderoy Lockhart wieder?",
|
||||||
|
"canonical_answer": "Janus-Thickey-Station (Langzeit-Fluchschäden)",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Janus-Thickey-Station (Langzeit-Fluchschäden)",
|
||||||
|
"Dai-Llewellyn-Station (Tierbisse)",
|
||||||
|
"Artefakt-Unfälle im Erdgeschoss",
|
||||||
|
"Teekessel-Verbrennungsstation"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Auf derselben geschlossenen Station im 4. Stock liegen auch Frank und Alice Longbottom.",
|
||||||
|
"question_en": "On which ward of St Mungo's Hospital do Harry, Ron, and Hermione bump into Gilderoy Lockhart in the fifth book?",
|
||||||
|
"answer_en": "Janus Thickey Ward (Long-Term Spell Damage)",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"Janus Thickey Ward (Long-Term Spell Damage)",
|
||||||
|
"Dai Llewellyn Ward (Creature Bites)",
|
||||||
|
"Artifact Accidents on the Ground Floor",
|
||||||
|
"Potion and Plant Poisoning Ward"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "Frank and Alice Longbottom are patients on the very same fourth-floor ward."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Welchen Beruf übt Charlie Weasley in Rumänien aus?",
|
||||||
|
"canonical_answer": "Drachenwärter",
|
||||||
|
"accepted_answers": json.dumps([
|
||||||
|
"Drachenwärter", "Drachenforscher", "Drachenzüchter", "Drachenpfleger",
|
||||||
|
"Er arbeitet mit Drachen", "Drachen", "Dragonologist", "Dragon keeper"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Er nimmt im 1. Buch den kleinen Norwegischen Stachelbuckel Norbert in seine Obhut.",
|
||||||
|
"question_en": "What is Charlie Weasley's profession in Romania?",
|
||||||
|
"answer_en": "Dragonkeeper",
|
||||||
|
"hint_en": "In the first book, his friends fly down to take Hagrid's Norwegian Ridgeback Norbert into their care."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Was tut Harry im Buch 'Die Heiligtümer des Todes' mit dem Elderstab, bevor er ihn an seinen endgültigen Ort zurückbringt?",
|
||||||
|
"canonical_answer": "Er repariert seinen eigenen zerbrochenen Zauberstab",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Er repariert seinen eigenen zerbrochenen Zauberstab",
|
||||||
|
"Er zerbricht ihn und wirft ihn von der Brücke",
|
||||||
|
"Er schenkt ihn Ron Weasley",
|
||||||
|
"Er übergibt ihn dem Zaubereiministerium"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Mit 'Reparo' gelingt ihm etwas, was Ollivander zuvor für unmöglich gehalten hatte.",
|
||||||
|
"question_en": "In the Deathly Hallows book, what does Harry do with the Elder Wand before returning it to Dumbledore's tomb?",
|
||||||
|
"answer_en": "He repairs his own broken holly and phoenix wand",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"He repairs his own broken holly and phoenix wand",
|
||||||
|
"He snaps it in half and throws it off the bridge",
|
||||||
|
"He gifts it to Ron Weasley",
|
||||||
|
"He locks it inside Gringotts"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "Using 'Reparo', he accomplishes something Ollivander thought was impossible."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wie heißt der Kobold, der im 4. Buch von Ludo Bagman mit Leprechaun-Gold betrogen wird?",
|
||||||
|
"canonical_answer": "Gornuk (bzw. Ragnok / Griphooks Kollegen)",
|
||||||
|
"options": json.dumps(["Ragnok", "Bogrod", "Travers", "Yaxley"], ensure_ascii=False),
|
||||||
|
"hint": "Er trifft sich im Eberkopf mit Ludo Bagman, um seine Schulden einzufordern.",
|
||||||
|
"question_en": "Which goblin confronts Ludo Bagman in the Hog's Head inn after being paid in vanishing Leprechaun gold?",
|
||||||
|
"answer_en": "Ragnok",
|
||||||
|
"options_en": json.dumps(["Ragnok", "Bogrod", "Travers", "Yaxley"], ensure_ascii=False),
|
||||||
|
"hint_en": "He meets Ludo Bagman in the Hog's Head to demand repayment of his bet."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Welches magische Radio-Programm hören Ron, Harry und Hermine im 7. Buch auf der Flucht, für das man ein wechselndes Passwort braucht?",
|
||||||
|
"canonical_answer": "PotterWatch",
|
||||||
|
"accepted_answers": json.dumps(["PotterWatch", "Potter Watch", "Potterwatch"]),
|
||||||
|
"hint": "Moderiert wird die Sendung von Lee Jordan unter dem Decknamen 'Fluss' (River).",
|
||||||
|
"question_en": "What is the name of the secret rebel radio broadcast that Harry, Ron, and Hermione listen to while on the run in book 7?",
|
||||||
|
"answer_en": "Potterwatch",
|
||||||
|
"hint_en": "The program is hosted by Lee Jordan under the codename 'River'."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "books",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wer war die ursprüngliche Besitzerin des Diadems von Ravenclaw und zugleich deren Tochter (die Graue Dame)?",
|
||||||
|
"canonical_answer": "Helena Ravenclaw",
|
||||||
|
"options": json.dumps(["Helena Ravenclaw", "Rowena Ravenclaw", "Helga Hufflepuff", "Millicent Bagnold"], ensure_ascii=False),
|
||||||
|
"hint": "Sie stahl das Diadem ihrer Mutter und versteckte es in einem hohlen Baum in Albanien.",
|
||||||
|
"question_en": "What is the real name of the Grey Lady, the ghost of Ravenclaw Tower and daughter of Rowena Ravenclaw?",
|
||||||
|
"answer_en": "Helena Ravenclaw",
|
||||||
|
"options_en": json.dumps(["Helena Ravenclaw", "Rowena Ravenclaw", "Helga Hufflepuff", "Millicent Bagnold"], ensure_ascii=False),
|
||||||
|
"hint_en": "She stole her mother's diadem and hid it inside a hollow tree in Albania."
|
||||||
|
},
|
||||||
|
|
||||||
|
# --- MOVIES (Filmwissen) ---
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "easy",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Welcher britische Schauspieler verkörperte Harry Potter in allen acht Kinofilmen?",
|
||||||
|
"canonical_answer": "Daniel Radcliffe",
|
||||||
|
"accepted_answers": json.dumps(["Daniel Radcliffe", "Radcliffe"]),
|
||||||
|
"hint": "Er war bei den Dreharbeiten zum ersten Film erst elf Jahre alt.",
|
||||||
|
"question_en": "Which British actor portrayed Harry Potter across all eight feature films?",
|
||||||
|
"answer_en": "Daniel Radcliffe",
|
||||||
|
"hint_en": "He was only eleven years old when filming began on the first movie."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welcher Regisseur inszenierte den dritten Kinofilm 'Harry Potter und der Gefangene von Askaban' (2004)?",
|
||||||
|
"canonical_answer": "Alfonso Cuarón",
|
||||||
|
"options": json.dumps(["Alfonso Cuarón", "Chris Columbus", "David Yates", "Mike Newell"], ensure_ascii=False),
|
||||||
|
"hint": "Der mexikanische Regisseur prägte den düsteren, künstlerischen Look des dritten Teils.",
|
||||||
|
"question_en": "Which director helmed the third movie, 'Harry Potter and the Prisoner of Azkaban' (2004)?",
|
||||||
|
"answer_en": "Alfonso Cuarón",
|
||||||
|
"options_en": json.dumps(["Alfonso Cuarón", "Chris Columbus", "David Yates", "Mike Newell"], ensure_ascii=False),
|
||||||
|
"hint_en": "The Mexican filmmaker gave the third installment its signature darker, atmospheric visual style."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Welcher weltberühmte Filmkomponist schrieb das ikonische Hauptthema 'Hedwig's Theme' für die ersten Harry-Potter-Filme?",
|
||||||
|
"canonical_answer": "John Williams",
|
||||||
|
"accepted_answers": json.dumps(["John Williams", "Williams"]),
|
||||||
|
"hint": "Er komponierte auch die Musik für Star Wars, Indiana Jones und Jurassic Park.",
|
||||||
|
"question_en": "Which legendary film composer wrote the iconic main theme 'Hedwig's Theme' for the first Harry Potter movies?",
|
||||||
|
"answer_en": "John Williams",
|
||||||
|
"hint_en": "He also composed the scores for Star Wars, Indiana Jones, and Jurassic Park."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welcher Schauspieler übernahm ab dem dritten Film die Rolle von Albus Dumbledore, nachdem Richard Harris verstorben war?",
|
||||||
|
"canonical_answer": "Michael Gambon",
|
||||||
|
"options": json.dumps(["Michael Gambon", "Ian McKellen", "Gary Oldman", "Ralph Fiennes"], ensure_ascii=False),
|
||||||
|
"hint": "Er spielte Dumbledore vom dritten bis zum achten Film.",
|
||||||
|
"question_en": "Which actor took over the role of Albus Dumbledore starting in the third film after Richard Harris passed away?",
|
||||||
|
"answer_en": "Michael Gambon",
|
||||||
|
"options_en": json.dumps(["Michael Gambon", "Ian McKellen", "Gary Oldman", "Ralph Fiennes"], ensure_ascii=False),
|
||||||
|
"hint_en": "He portrayed Dumbledore from Prisoner of Azkaban through Deathly Hallows – Part 2."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "easy",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Über welches berühmte schottische Eisenbahnviadukt fährt der Hogwarts-Express in den Filmen?",
|
||||||
|
"canonical_answer": "Glenfinnan-Viadukt",
|
||||||
|
"options": json.dumps(["Glenfinnan-Viadukt", "Ribblehead-Viadukt", "Clifton-Brücke", "Forth Bridge"], ensure_ascii=False),
|
||||||
|
"hint": "Es liegt in den schottischen Highlands am Loch Shiel.",
|
||||||
|
"question_en": "Which famous Scottish railway viaduct does the Hogwarts Express cross in the movies?",
|
||||||
|
"answer_en": "Glenfinnan Viaduct",
|
||||||
|
"options_en": json.dumps(["Glenfinnan Viaduct", "Ribblehead Viaduct", "Clifton Bridge", "Forth Bridge"], ensure_ascii=False),
|
||||||
|
"hint_en": "It is located in the Scottish Highlands overlooking Loch Shiel."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welche Augenfarbe hat Harry Potter in den Kinofilmen (weil Daniel Radcliffe die farbigen Kontaktlinsen nicht vertrug), obwohl sie in den Büchern leuchtend grün sind?",
|
||||||
|
"canonical_answer": "Blau",
|
||||||
|
"options": json.dumps(["Blau", "Braun", "Grau", "Bernsteinfarben"], ensure_ascii=False),
|
||||||
|
"hint": "Daniel Radcliffes natürliche Augenfarbe.",
|
||||||
|
"question_en": "What color are Harry Potter's eyes in the movies (because Daniel Radcliffe had an allergic reaction to contact lenses), even though they are bright green in the books?",
|
||||||
|
"answer_en": "Blue",
|
||||||
|
"options_en": json.dumps(["Blue", "Brown", "Hazel", "Amber"], ensure_ascii=False),
|
||||||
|
"hint_en": "It is Daniel Radcliffe's natural eye color."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Welcher unvergessene britische Schauspieler verkörperte Severus Snape in allen acht Filmen?",
|
||||||
|
"canonical_answer": "Alan Rickman",
|
||||||
|
"accepted_answers": json.dumps(["Alan Rickman", "Rickman"]),
|
||||||
|
"hint": "J.K. Rowling verriet ihm schon vor dem Erscheinen des letzten Buches das Geheimnis hinter Snapes 'Always'.",
|
||||||
|
"question_en": "Which beloved British actor portrayed Severus Snape across all eight Harry Potter films?",
|
||||||
|
"answer_en": "Alan Rickman",
|
||||||
|
"hint_en": "J.K. Rowling privately told him the secret behind Snape's 'Always' years before the final book was published."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "An welchem historischen englischen Bahnhof wurden die Außenaufnahmen von Hogsmeade Station in den ersten Filmen gedreht?",
|
||||||
|
"canonical_answer": "Goathland Station (North Yorkshire)",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Goathland Station (North Yorkshire)",
|
||||||
|
"Paddington Station (London)",
|
||||||
|
"Waverley Station (Edinburgh)",
|
||||||
|
"York Central Station"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Eine malerische Museumsbahnstation der North Yorkshire Moors Railway.",
|
||||||
|
"question_en": "At which historic English heritage railway station were the Hogsmeade Station scenes filmed in the early movies?",
|
||||||
|
"answer_en": "Goathland Station (North Yorkshire)",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"Goathland Station (North Yorkshire)",
|
||||||
|
"Paddington Station (London)",
|
||||||
|
"Waverley Station (Edinburgh)",
|
||||||
|
"York Central Station"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "A picturesque station on the North Yorkshire Moors Railway."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Auf welchem echten Londoner Bahnhof wurden die Szenen am Gleis 9¾ gedreht?",
|
||||||
|
"canonical_answer": "King's Cross",
|
||||||
|
"options": json.dumps(["King's Cross", "Waterloo", "Victoria Station", "Euston"], ensure_ascii=False),
|
||||||
|
"hint": "Die Außenfassade im 2. Film zeigt das benachbarte St Pancras Renaissance Hotel.",
|
||||||
|
"question_en": "At which real-life London railway station were the Platform 9¾ scenes filmed?",
|
||||||
|
"answer_en": "King's Cross",
|
||||||
|
"options_en": json.dumps(["King's Cross", "Waterloo", "Victoria Station", "Euston"], ensure_ascii=False),
|
||||||
|
"hint_en": "Today, fans can take photos with a luggage trolley disappearing into the brick wall there."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "movies",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wie heißt die fiktive Zauberrockband im 4. Film beim Weihnachtsball, deren Sänger von Pulp-Frontmann Jarvis Cocker gespielt wurde?",
|
||||||
|
"canonical_answer": "The Weird Sisters (Schwestern des Schicksals)",
|
||||||
|
"options": json.dumps([
|
||||||
|
"The Weird Sisters (Schwestern des Schicksals)",
|
||||||
|
"Celestina Warbeck & the Banshees",
|
||||||
|
"The Hobgoblins",
|
||||||
|
"Frog Choir"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Auch Mitglieder der Band Radiohead spielten in dieser Filmszene in der Band mit!",
|
||||||
|
"question_en": "What is the name of the wizarding rock band at the Yule Ball in the fourth movie, fronted by Pulp's Jarvis Cocker?",
|
||||||
|
"answer_en": "The Weird Sisters",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"The Weird Sisters",
|
||||||
|
"Celestina Warbeck & the Banshees",
|
||||||
|
"The Hobgoblins",
|
||||||
|
"The Frog Choir"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "Members of Radiohead also appeared in the band lineup for that scene!"
|
||||||
|
},
|
||||||
|
|
||||||
|
# --- SPELLS & POTIONS (Zaubersprüche & Tränke) ---
|
||||||
|
{
|
||||||
|
"category": "spells",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Wie lautet der Gegenzauber zum Schwebezauber 'Levicorpus', mit dem man das Opfer wieder auf den Boden holt?",
|
||||||
|
"canonical_answer": "Liberacorpus",
|
||||||
|
"accepted_answers": json.dumps(["Liberacorpus", "Libera corpus"]),
|
||||||
|
"hint": "Harry findet sowohl den Zauber als auch den Gegenzauber handschriftlich im Buch des Halbblutprinzen.",
|
||||||
|
"question_en": "What is the counter-jinx to 'Levicorpus' that lowers the victim back to the ground?",
|
||||||
|
"answer_en": "Liberacorpus",
|
||||||
|
"hint_en": "Harry discovers both the spell and its counter-jinx written in the margins of the Half-Blood Prince's textbook."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "spells",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welcher seltene Stein aus dem Magen einer Ziege wirkt als universelles Gegengift gegen die meisten Zaubertrank-Gifte?",
|
||||||
|
"canonical_answer": "Bezoar",
|
||||||
|
"options": json.dumps(["Bezoar", "Mondstein", "Blutstein", "Flussstein"], ensure_ascii=False),
|
||||||
|
"hint": "Snape fragt Harry danach in seiner allerersten Zaubertränkestunde, und Harry rettet damit später Rons Leben.",
|
||||||
|
"question_en": "Which stone taken from the stomach of a goat acts as an antidote to most poisons?",
|
||||||
|
"answer_en": "Bezoar",
|
||||||
|
"options_en": json.dumps(["Bezoar", "Moonstone", "Bloodstone", "Runestone"], ensure_ascii=False),
|
||||||
|
"hint_en": "Snape quizzes Harry about it in his very first Potions lesson, and Harry later uses one to save Ron's life."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "spells",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Welcher dunkle Fluch, den der Halbblutprinz erfunden hat, schlitzt das Opfer wie mit einem unsichtbaren Schwert auf ('Für Feinde')?",
|
||||||
|
"canonical_answer": "Sectumsempra",
|
||||||
|
"accepted_answers": json.dumps(["Sectumsempra", "Sectum Sempra"]),
|
||||||
|
"hint": "Harry wendet ihn im 6. Teil im Waschraum gegen Draco Malfoy an.",
|
||||||
|
"question_en": "Which dark curse invented by the Half-Blood Prince slashes the target as if with an invisible sword ('For Enemies')?",
|
||||||
|
"answer_en": "Sectumsempra",
|
||||||
|
"hint_en": "Harry uses it against Draco Malfoy in the bathroom in year six."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "spells",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wie lange dauert die aufwendige Zubereitung des Vielsaft-Tranks (Polyjuice Potion) ungefähr?",
|
||||||
|
"canonical_answer": "Einen ganzen Monat",
|
||||||
|
"options": json.dumps(["Einen ganzen Monat", "Genau 24 Stunden", "Sieben Tage", "Nur eine Vollmondnacht"], ensure_ascii=False),
|
||||||
|
"hint": "Unter anderem muss Florfliegenkraut 21 Tage lang schmoren.",
|
||||||
|
"question_en": "Approximately how long does it take to brew Polyjuice Potion from start to finish?",
|
||||||
|
"answer_en": "One full month",
|
||||||
|
"options_en": json.dumps(["One full month", "Exactly 24 hours", "Seven days", "A single full-moon night"], ensure_ascii=False),
|
||||||
|
"hint_en": "Lacewing flies alone must stew for twenty-one days."
|
||||||
|
},
|
||||||
|
|
||||||
|
# --- HOGWARTS & PLACES ---
|
||||||
|
{
|
||||||
|
"category": "hogwarts",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wie gelangt man in die Schulküche von Hogwarts, die direkt unter der Großen Halle liegt?",
|
||||||
|
"canonical_answer": "Man kitzelt die Birne auf einem Obst-Gemälde",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Man kitzelt die Birne auf einem Obst-Gemälde",
|
||||||
|
"Man klopft dreimal auf ein Essigfass",
|
||||||
|
"Man sagt das Passwort 'Kürbispastete' zu einer Rüstung",
|
||||||
|
"Man zieht an einer goldenen Bratpfanne"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Die Frucht kichert daraufhin und verwandelt sich in einen grünen Türgriff.",
|
||||||
|
"question_en": "How do you open the secret entrance to the Hogwarts kitchens located beneath the Great Hall?",
|
||||||
|
"answer_en": "Tickle the pear in the painting of a bowl of fruit",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"Tickle the pear in the painting of a bowl of fruit",
|
||||||
|
"Tap a vinegar barrel in the rhythm of Helga Hufflepuff",
|
||||||
|
"Whisper 'Pumpkin Pasties' to a suit of armor",
|
||||||
|
"Pull on a golden ladle"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "The fruit giggles and transforms into a green door handle."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "hogwarts",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Was unterscheidet den Eingang zum Ravenclaw-Gemeinschaftsraum von denen der anderen drei Häuser?",
|
||||||
|
"canonical_answer": "Man muss eine philosophische Rätselfrage des Türklopfers beantworten statt ein Passwort zu nennen",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Man muss eine philosophische Rätselfrage des Türklopfers beantworten statt ein Passwort zu nennen",
|
||||||
|
"Man kann nur auf einem Besen durch das Turmfenster hineinfliegen",
|
||||||
|
"Man muss seinen Zauberstab auf eine Waage legen",
|
||||||
|
"Die Tür öffnet sich nur bei Mondschein"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Der bronzene Adler-Türklopfer fragt z. B.: 'Was war zuerst da, der Phönix oder die Flamme?'",
|
||||||
|
"question_en": "How does entering the Ravenclaw common room differ from the other three Hogwarts houses?",
|
||||||
|
"answer_en": "You must answer a philosophical riddle posed by a bronze eagle knocker instead of giving a password",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"You must answer a philosophical riddle posed by a bronze eagle knocker instead of giving a password",
|
||||||
|
"You can only enter by flying through the tower window on a broom",
|
||||||
|
"You must weigh your wand on an enchanted scale",
|
||||||
|
"The door only opens under moonlight"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "For example, the bronze eagle asks: 'Which came first, the phoenix or the flame?'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "hogwarts",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Unter welchem Baum auf dem Schulgelände von Hogwarts beginnt der geheime Tunnel, der direkt in die Heulende Hütte nach Hogsmeade führt?",
|
||||||
|
"canonical_answer": "Peitschende Weide",
|
||||||
|
"accepted_answers": json.dumps(["Peitschende Weide", "Die Peitschende Weide", "Whomping Willow"]),
|
||||||
|
"hint": "Er wurde gepflanzt, als Remus Lupin als Schüler nach Hogwarts kam.",
|
||||||
|
"question_en": "Beneath which violent tree on the Hogwarts grounds is the secret tunnel leading to the Shrieking Shack?",
|
||||||
|
"answer_en": "Whomping Willow",
|
||||||
|
"hint_en": "It was planted the year Remus Lupin arrived at Hogwarts as a student."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "hogwarts",
|
||||||
|
"difficulty": "easy",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Wie heißt das einzige komplett von Zauberern bewohnte Dorf in Großbritannien, das in der Nähe von Hogwarts liegt?",
|
||||||
|
"canonical_answer": "Hogsmeade",
|
||||||
|
"accepted_answers": json.dumps(["Hogsmeade"]),
|
||||||
|
"hint": "Schüler ab der dritten Klasse dürfen es an bestimmten Wochenenden mit einer Unterschrift besuchen.",
|
||||||
|
"question_en": "What is the name of the only all-wizarding village in Britain, located near Hogwarts?",
|
||||||
|
"answer_en": "Hogsmeade",
|
||||||
|
"hint_en": "Students in their third year and above may visit it on certain weekends with a signed permission slip."
|
||||||
|
},
|
||||||
|
|
||||||
|
# --- QUIDDITCH ---
|
||||||
|
{
|
||||||
|
"category": "quidditch",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welches spektakuläre Flugmanöver führt Viktor Krum beim Finale der Quidditch-Weltmeisterschaft 1994 aus, um den gegnerischen Sucher Aidan Lynch in den Boden stürzen zu lassen?",
|
||||||
|
"canonical_answer": "Wronski-Bluff (Wronski Feint)",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Wronski-Bluff (Wronski Feint)",
|
||||||
|
"Porskoff-Ploy",
|
||||||
|
"Falkenkopf-Angriffsformation",
|
||||||
|
"Faultier-Rolle (Sloth Grip Roll)"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Der Sucher täuscht im Sturzflug vor, den Goldenen Schnatz am Boden gesehen zu haben, und zieht im letzten Moment hoch.",
|
||||||
|
"question_en": "Which famous diving maneuver does Viktor Krum execute during the 1994 Quidditch World Cup Final to trick Irish Seeker Aidan Lynch?",
|
||||||
|
"answer_en": "Wronski Feint",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"Wronski Feint",
|
||||||
|
"Porskoff Ploy",
|
||||||
|
"Hawkshead Attacking Formation",
|
||||||
|
"Sloth Grip Roll"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "The Seeker dives steeply toward the ground pretending to have spotted the Snitch, pulling up at the very last second."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "quidditch",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welches magische Wesen war vor der Erfindung des Goldenen Schnatzes das ursprüngliche Jagdziel beim Quidditch?",
|
||||||
|
"canonical_answer": "Der Goldene Schnatzer (Golden Snidget)",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Der Goldene Schnatzer (Golden Snidget)",
|
||||||
|
"Der Fwuuper",
|
||||||
|
"Der Augurey",
|
||||||
|
"Der Jobberknoll"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Ein kugelrunder kleiner goldener Vogel, der dadurch fast ausgerottet wurde, bevor Bowman Wright den metallenen Schnatz schmiedete.",
|
||||||
|
"question_en": "Before the invention of the metallic Golden Snitch, which small magical bird was hunted during Quidditch matches?",
|
||||||
|
"answer_en": "Golden Snidget",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"Golden Snidget",
|
||||||
|
"Fwooper",
|
||||||
|
"Augurey",
|
||||||
|
"Jobberknoll"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "The tiny spherical golden bird was nearly hunted to extinction before Bowman Wright forged the enchanted metal replacement."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "quidditch",
|
||||||
|
"difficulty": "medium",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Welche beiden Nationalmannschaften standen sich im Finale der Quidditch-Weltmeisterschaft 1994 gegenüber?",
|
||||||
|
"canonical_answer": "Irland und Bulgarien",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Irland und Bulgarien",
|
||||||
|
"England und Transsilvanien",
|
||||||
|
"Schottland und Frankreich",
|
||||||
|
"Irland und Rumänien"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Viktor Krum fing zwar den Schnatz, doch die andere Mannschaft gewann trotzdem mit 170 zu 160 Punkten!",
|
||||||
|
"question_en": "Which two national teams faced off in the final of the 1994 Quidditch World Cup?",
|
||||||
|
"answer_en": "Ireland and Bulgaria",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"Ireland and Bulgaria",
|
||||||
|
"England and Transylvania",
|
||||||
|
"Scotland and France",
|
||||||
|
"Ireland and Romania"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "Viktor Krum caught the Snitch, yet the opposing team still won the match 170 to 160!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "quidditch",
|
||||||
|
"difficulty": "easy",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "Wie heißt das schnellste Rennbesen-Modell der Welt, das Harry im dritten Schuljahr anonym von seinem Paten Sirius Black geschenkt bekommt?",
|
||||||
|
"canonical_answer": "Feuerblitz",
|
||||||
|
"accepted_answers": json.dumps(["Feuerblitz", "Der Feuerblitz", "Firebolt"]),
|
||||||
|
"hint": "Er löst Harrys zerstörten Nimbus 2000 ab.",
|
||||||
|
"question_en": "What is the name of the world-class racing broom Harry receives anonymously from his godfather Sirius Black in his third year?",
|
||||||
|
"answer_en": "Firebolt",
|
||||||
|
"hint_en": "It replaces Harry's destroyed Nimbus 2000."
|
||||||
|
},
|
||||||
|
|
||||||
|
# --- CHARACTERS ---
|
||||||
|
{
|
||||||
|
"category": "characters",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "text",
|
||||||
|
"question": "In welches Tier verwandelt sich Rita Kimmkorn (Rita Skeeter) als nicht registrierter Animagus, um unbemerkt Gespräche zu belauschen?",
|
||||||
|
"canonical_answer": "Käfer",
|
||||||
|
"accepted_answers": json.dumps(["Käfer", "Ein Käfer", "Wasserkäfer", "Grüner Käfer", "Beetle"]),
|
||||||
|
"hint": "Hermine fängt sie am Ende des 4. Schuljahres in einem unzerbrechlichen Schraubglas.",
|
||||||
|
"question_en": "Into which insect does journalist Rita Skeeter transform as an unregistered Animagus to eavesdrop on conversations?",
|
||||||
|
"answer_en": "Beetle",
|
||||||
|
"hint_en": "Hermione traps her inside an unbreakable glass jar at the end of year four."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "characters",
|
||||||
|
"difficulty": "hard",
|
||||||
|
"question_type": "multiple_choice",
|
||||||
|
"question": "Wer hinterließ die geheimnisvolle Notiz mit den Initialen 'R.A.B.' in der falschen Horkrux-Höhle?",
|
||||||
|
"canonical_answer": "Regulus Arcturus Black",
|
||||||
|
"options": json.dumps([
|
||||||
|
"Regulus Arcturus Black",
|
||||||
|
"Rodolphus Augustus Bell",
|
||||||
|
"Rufus Albus Bones",
|
||||||
|
"Rabastan Lestrange"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint": "Der jüngere Bruder von Sirius Black, der mit Hilfe des Hauselfen Kreacher das echte Medaillon stahl.",
|
||||||
|
"question_en": "Who left the mysterious note signed 'R.A.B.' inside the fake Horcrux locket in the sea cave?",
|
||||||
|
"answer_en": "Regulus Arcturus Black",
|
||||||
|
"options_en": json.dumps([
|
||||||
|
"Regulus Arcturus Black",
|
||||||
|
"Rodolphus Augustus Bell",
|
||||||
|
"Rufus Albus Bones",
|
||||||
|
"Rabastan Lestrange"
|
||||||
|
], ensure_ascii=False),
|
||||||
|
"hint_en": "Sirius Black's younger brother, who stole the real locket with the help of the house-elf Kreacher."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def seed_lore_pack():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
existing_questions = {q.question.strip().lower(): q for q in db.query(Question).all()}
|
||||||
|
max_sort = db.query(Question.sort_order).order_by(Question.sort_order.desc()).first()
|
||||||
|
next_sort = (max_sort[0] + 1) if max_sort and max_sort[0] else 100
|
||||||
|
|
||||||
|
added = 0
|
||||||
|
for item in LORE_PACK_QUESTIONS:
|
||||||
|
q_key = item["question"].strip().lower()
|
||||||
|
if q_key in existing_questions:
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_q = Question(
|
||||||
|
category=item["category"],
|
||||||
|
difficulty=item["difficulty"],
|
||||||
|
question_type=item["question_type"],
|
||||||
|
question=item["question"],
|
||||||
|
canonical_answer=item["canonical_answer"],
|
||||||
|
accepted_answers=item.get("accepted_answers"),
|
||||||
|
options=item.get("options"),
|
||||||
|
hint=item.get("hint"),
|
||||||
|
question_en=item.get("question_en"),
|
||||||
|
answer_en=item.get("answer_en"),
|
||||||
|
options_en=item.get("options_en"),
|
||||||
|
hint_en=item.get("hint_en"),
|
||||||
|
status="active",
|
||||||
|
sort_order=next_sort,
|
||||||
|
submitted_by="System Lore Pack"
|
||||||
|
)
|
||||||
|
db.add(new_q)
|
||||||
|
next_sort += 1
|
||||||
|
added += 1
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
print(f"Lore Pack Seeding complete: {added} new questions added. Total active: {db.query(Question).filter(Question.status == 'active').count()}")
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
seed_lore_pack()
|
||||||
+1465
-1765
File diff suppressed because it is too large
Load Diff
+606
-586
File diff suppressed because it is too large
Load Diff
+25
-17
@@ -1,18 +1,20 @@
|
|||||||
const SUGGESTION_TEXT = {
|
const SUGGESTION_TEXT = {
|
||||||
de: {
|
de: {
|
||||||
title: "✨ Neue Frage für das Quiz vorschlagen ✨",
|
title: "✍️ Neue Quiz-Frage einreichen",
|
||||||
subtitle: "Bereichere das magische Quiz mit kniffligen Harry-Potter- oder Escape-Rätseln!",
|
subtitle: "Bereichere PotterTrivia mit deinen eigenen Fragen zu Büchern, Filmen oder magischen Geschöpfen!",
|
||||||
alert: "ℹ️ <strong>Hinweis zur Moderation:</strong> Eingereichte Fragen werden nicht sofort öffentlich sichtbar, sondern erhalten automatisch den Status <em>„Prüfung ausstehend“</em>. Unser Zaubereiministerium prüft und testet die Antwort, bevor die Frage freigeschaltet wird.",
|
alert: "ℹ️ <strong>Hinweis zur Moderation:</strong> Eingereichte Fragen werden kurz auf Richtigkeit geprüft, bevor sie im öffentlichen Quiz-Pool freigeschaltet werden.",
|
||||||
nameLabel: "Dein Name / Zauberer-Alias (optional):",
|
nameLabel: "Dein Name / Alias (optional):",
|
||||||
namePlaceholder: "z. B. Hermine Granger",
|
namePlaceholder: "z. B. Hermine Granger",
|
||||||
catLabel: "Kategorie / Themenbereich:",
|
catLabel: "Kategorie / Themenbereich:",
|
||||||
catHogwarts: "🏰 Hogwarts & Schulgelände",
|
catBooks: "📖 Buchwissen (Buch-Details)",
|
||||||
catCharacters: "🧙 Charaktere & Personen",
|
catMovies: "🎬 Filmwissen (Kino & Drehorte)",
|
||||||
catSpells: "⚡ Zaubersprüche & Magie",
|
catHogwarts: "🏰 Hogwarts & Orte",
|
||||||
catCreatures: "🐉 Magische Tierwesen",
|
catCharacters: "🧙♂️ Charaktere & Personen",
|
||||||
|
catSpells: "🪄 Zaubersprüche & Tränke",
|
||||||
|
catCreatures: "🐉 Magische Geschöpfe",
|
||||||
catQuidditch: "🧹 Quidditch & Sport",
|
catQuidditch: "🧹 Quidditch & Sport",
|
||||||
catLore: "🔮 Artefakte & Geschichte",
|
catLore: "🔮 Artefakte & Geschichte",
|
||||||
catGeneral: "📜 Allgemeines Zauberwissen",
|
catGeneral: "⚡ Allgemeines Wissen",
|
||||||
frageLabel: "Deine Frage: *",
|
frageLabel: "Deine Frage: *",
|
||||||
fragePlaceholder: "z. B. Wie heißt der Schulleiter von Hogwarts?",
|
fragePlaceholder: "z. B. Wie heißt der Schulleiter von Hogwarts?",
|
||||||
diffLabel: "Schwierigkeitsgrad:",
|
diffLabel: "Schwierigkeitsgrad:",
|
||||||
@@ -36,19 +38,21 @@ const SUGGESTION_TEXT = {
|
|||||||
successHeader: "✨ Vorschlag erfolgreich übermittelt! ✨"
|
successHeader: "✨ Vorschlag erfolgreich übermittelt! ✨"
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
title: "✨ Suggest a New Quiz Question ✨",
|
title: "✍️ Submit a New Quiz Question",
|
||||||
subtitle: "Enrich the magical quiz with tricky Harry Potter or escape puzzles!",
|
subtitle: "Enrich PotterTrivia with your own questions about the books, movies, or magical creatures!",
|
||||||
alert: "ℹ️ <strong>Moderation Note:</strong> Submitted questions are not immediately public; they automatically receive the status <em>'Pending Review'</em>. The Ministry of Magic tests and verifies the answer before approval.",
|
alert: "ℹ️ <strong>Moderation Note:</strong> Submitted questions are briefly reviewed for accuracy before going live in the public quiz pool.",
|
||||||
nameLabel: "Your Name / Wizard Alias (optional):",
|
nameLabel: "Your Name / Alias (optional):",
|
||||||
namePlaceholder: "e.g. Hermione Granger",
|
namePlaceholder: "e.g. Hermione Granger",
|
||||||
catLabel: "Category / Topic Area:",
|
catLabel: "Category / Topic Area:",
|
||||||
catHogwarts: "🏰 Hogwarts & School Grounds",
|
catBooks: "📖 Book Lore (Book Details)",
|
||||||
catCharacters: "🧙 Characters & People",
|
catMovies: "🎬 Movie Trivia (Cinema & Filming)",
|
||||||
catSpells: "⚡ Spells & Magic",
|
catHogwarts: "🏰 Hogwarts & Places",
|
||||||
|
catCharacters: "🧙♂️ Characters & People",
|
||||||
|
catSpells: "🪄 Spells & Potions",
|
||||||
catCreatures: "🐉 Magical Creatures",
|
catCreatures: "🐉 Magical Creatures",
|
||||||
catQuidditch: "🧹 Quidditch & Sport",
|
catQuidditch: "🧹 Quidditch & Sport",
|
||||||
catLore: "🔮 Artifacts & Lore",
|
catLore: "🔮 Artifacts & Lore",
|
||||||
catGeneral: "📜 General Wizarding Lore",
|
catGeneral: "⚡ General Knowledge",
|
||||||
frageLabel: "Your Question: *",
|
frageLabel: "Your Question: *",
|
||||||
fragePlaceholder: "e.g. Who is the Headmaster of Hogwarts?",
|
fragePlaceholder: "e.g. Who is the Headmaster of Hogwarts?",
|
||||||
diffLabel: "Difficulty Level:",
|
diffLabel: "Difficulty Level:",
|
||||||
@@ -92,6 +96,8 @@ function onLanguageChange(lang) {
|
|||||||
const submitBtn = document.getElementById("submitBtn");
|
const submitBtn = document.getElementById("submitBtn");
|
||||||
|
|
||||||
const catLabel = document.getElementById("suggCatLabel");
|
const catLabel = document.getElementById("suggCatLabel");
|
||||||
|
const catBooks = document.getElementById("catBooks");
|
||||||
|
const catMovies = document.getElementById("catMovies");
|
||||||
const catHogwarts = document.getElementById("catHogwarts");
|
const catHogwarts = document.getElementById("catHogwarts");
|
||||||
const catCharacters = document.getElementById("catCharacters");
|
const catCharacters = document.getElementById("catCharacters");
|
||||||
const catSpells = document.getElementById("catSpells");
|
const catSpells = document.getElementById("catSpells");
|
||||||
@@ -106,6 +112,8 @@ function onLanguageChange(lang) {
|
|||||||
if (nameLabel) nameLabel.innerText = t.nameLabel;
|
if (nameLabel) nameLabel.innerText = t.nameLabel;
|
||||||
if (nameInput) nameInput.placeholder = t.namePlaceholder;
|
if (nameInput) nameInput.placeholder = t.namePlaceholder;
|
||||||
if (catLabel) catLabel.innerText = t.catLabel;
|
if (catLabel) catLabel.innerText = t.catLabel;
|
||||||
|
if (catBooks) catBooks.innerText = t.catBooks;
|
||||||
|
if (catMovies) catMovies.innerText = t.catMovies;
|
||||||
if (catHogwarts) catHogwarts.innerText = t.catHogwarts;
|
if (catHogwarts) catHogwarts.innerText = t.catHogwarts;
|
||||||
if (catCharacters) catCharacters.innerText = t.catCharacters;
|
if (catCharacters) catCharacters.innerText = t.catCharacters;
|
||||||
if (catSpells) catSpells.innerText = t.catSpells;
|
if (catSpells) catSpells.innerText = t.catSpells;
|
||||||
|
|||||||
@@ -225,9 +225,15 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="category">Kategorie:</label>
|
<label for="category">Kategorie:</label>
|
||||||
<select name="category" id="category" class="input-text">
|
<select name="category" id="category" class="input-text">
|
||||||
<option value="harry_potter" selected>Harry Potter / Escape</option>
|
<option value="books">📖 Buchwissen</option>
|
||||||
<option value="insider">Persönlich / Insider</option>
|
<option value="movies">🎬 Filmwissen</option>
|
||||||
<option value="general">Allgemeines Rätsel</option>
|
<option value="creatures">🐉 Magische Geschöpfe</option>
|
||||||
|
<option value="spells">🪄 Zaubersprüche & Tränke</option>
|
||||||
|
<option value="characters" selected>🧙♂️ Charaktere</option>
|
||||||
|
<option value="hogwarts">🏰 Hogwarts & Orte</option>
|
||||||
|
<option value="quidditch">🧹 Quidditch</option>
|
||||||
|
<option value="lore">🔮 Artefakte & Lore</option>
|
||||||
|
<option value="general">⚡ Allgemein</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+117
-167
@@ -1,167 +1,117 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
|
||||||
<title>{% block title %}Magisches Harry-Potter-Escape-Quiz{% endblock %}</title>
|
<title>{% block title %}PotterTrivia – Das Harry-Potter-Wissensquiz{% endblock %}</title>
|
||||||
<link rel="icon" href="/static/favicon.ico">
|
<link rel="icon" href="/static/favicon.ico">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@500;700&family=Marck+Script&family=MedievalSharp&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Space+Grotesk:wght@600;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="/static/css/style.css?v=3.5">
|
<link rel="stylesheet" href="/static/css/style.css?v=4.0">
|
||||||
{% block head %}{% endblock %}
|
{% block head %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<audio id="bgm" src="/static/audio/music.mp3" loop preload="auto"></audio>
|
<header class="site-header">
|
||||||
|
<div class="header-inner">
|
||||||
<header class="site-header">
|
<a href="/" class="brand-link">
|
||||||
<div class="header-inner">
|
<span class="brand-badge">⚡</span>
|
||||||
<a href="/" class="brand-link">
|
<span class="brand-title">Potter<span class="brand-accent">Trivia</span></span>
|
||||||
<span class="brand-sparkle">⚡</span>
|
</a>
|
||||||
<span class="brand-title">Hogwarts Quiz</span>
|
|
||||||
</a>
|
<nav id="navLinks" class="nav-links">
|
||||||
|
<a href="/" class="nav-link" id="navQuiz" onclick="closeMobileMenu()">🎯 Quiz</a>
|
||||||
<div class="header-right">
|
<a href="/bestenliste" class="nav-link" id="navLeaderboard" onclick="closeMobileMenu()">🏆 Bestenliste</a>
|
||||||
<div class="lang-switch-group" role="group" aria-label="Sprachauswahl / Language">
|
<a href="/vorschlag" class="nav-link" id="navSuggest" onclick="closeMobileMenu()">✍️ Frage einreichen</a>
|
||||||
<button type="button" id="langDe" class="lang-btn active" onclick="setLanguage('de')" title="Deutsch">🇩🇪 DE</button>
|
<a href="/admin" class="nav-link" id="navAdmin" onclick="closeMobileMenu()">⚙️ Admin</a>
|
||||||
<button type="button" id="langEn" class="lang-btn" onclick="setLanguage('en')" title="English">🇬🇧 EN</button>
|
</nav>
|
||||||
</div>
|
|
||||||
|
<div class="header-right">
|
||||||
<button id="audioToggle" class="audio-btn" title="Musik an/aus" onclick="toggleAudio()">
|
<div class="lang-switch-group" role="group" aria-label="Sprachauswahl / Language">
|
||||||
<span id="audioIcon">🔇</span>
|
<button type="button" id="langDe" class="lang-btn active" onclick="setLanguage('de')" title="Deutsch">🇩🇪 DE</button>
|
||||||
<span id="audioText" class="audio-label">Musik: Aus</span>
|
<button type="button" id="langEn" class="lang-btn" onclick="setLanguage('en')" title="English">🇬🇧 EN</button>
|
||||||
</button>
|
</div>
|
||||||
|
|
||||||
<button id="menuToggle" class="menu-toggle-btn" aria-label="Menü öffnen" onclick="toggleMobileMenu()">
|
<button id="menuToggle" class="menu-toggle-btn" aria-label="Menü öffnen" onclick="toggleMobileMenu()">
|
||||||
<span id="menuIcon">☰</span>
|
<span id="menuIcon">☰</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<nav id="navLinks" class="nav-links">
|
</header>
|
||||||
<a href="/" class="nav-link" id="navQuiz" onclick="closeMobileMenu()">✨ Zum Quiz</a>
|
|
||||||
<a href="https://bootle.dev.waiswuaschd.de" class="nav-link" id="navEscape" target="_blank" rel="noopener">🧪 Escape Adventure</a>
|
<main class="container">
|
||||||
<a href="/bestenliste" class="nav-link" id="navLeaderboard" onclick="closeMobileMenu()">🏆 Bestenliste</a>
|
{% block content %}{% endblock %}
|
||||||
<a href="/vorschlag" class="nav-link" id="navSuggest" onclick="closeMobileMenu()">✍️ Frage einreichen</a>
|
</main>
|
||||||
<a href="/admin" class="nav-link" id="navAdmin" onclick="closeMobileMenu()">🔒 Admin-Cockpit</a>
|
|
||||||
|
<footer class="footer">
|
||||||
</nav>
|
<p id="footerText">⚡ PotterTrivia · Das unabhängige Harry-Potter-Wissensquiz</p>
|
||||||
</div>
|
<div class="footer-links">
|
||||||
</header>
|
<a href="/impressum" class="footer-link" id="footerImpressum">⚖️ Impressum</a>
|
||||||
|
<span class="footer-sep">·</span>
|
||||||
<main class="container">
|
<a href="/datenschutz" class="footer-link" id="footerDatenschutz">🛡️ Datenschutz</a>
|
||||||
{% block content %}{% endblock %}
|
</div>
|
||||||
</main>
|
</footer>
|
||||||
|
|
||||||
<footer class="footer">
|
<script>
|
||||||
<p id="footerText">⚡ Magisches Hogwarts-Escape-Quiz · Betrieben mit Liebe & Zauberkraft ⚡</p>
|
const navLinks = document.getElementById("navLinks");
|
||||||
<div class="footer-links">
|
const menuIcon = document.getElementById("menuIcon");
|
||||||
<a href="/impressum" class="footer-link" id="footerImpressum">⚖️ Impressum</a>
|
|
||||||
<span class="footer-sep">·</span>
|
let currentLang = localStorage.getItem("quiz_lang") || "de";
|
||||||
<a href="/datenschutz" class="footer-link" id="footerDatenschutz">🛡️ Datenschutz</a>
|
|
||||||
</div>
|
function setLanguage(lang) {
|
||||||
</footer>
|
currentLang = (lang === "en") ? "en" : "de";
|
||||||
|
localStorage.setItem("quiz_lang", currentLang);
|
||||||
<script>
|
document.documentElement.lang = currentLang;
|
||||||
const bgm = document.getElementById("bgm");
|
updateLanguageUI();
|
||||||
const audioIcon = document.getElementById("audioIcon");
|
if (typeof onLanguageChange === "function") {
|
||||||
const audioText = document.getElementById("audioText");
|
onLanguageChange(currentLang);
|
||||||
const navLinks = document.getElementById("navLinks");
|
}
|
||||||
const menuIcon = document.getElementById("menuIcon");
|
}
|
||||||
|
|
||||||
let currentLang = localStorage.getItem("quiz_lang") || "de";
|
function updateLanguageUI() {
|
||||||
|
const isDe = currentLang === "de";
|
||||||
function setLanguage(lang) {
|
const deBtn = document.getElementById("langDe");
|
||||||
currentLang = (lang === "en") ? "en" : "de";
|
const enBtn = document.getElementById("langEn");
|
||||||
localStorage.setItem("quiz_lang", currentLang);
|
if (deBtn && enBtn) {
|
||||||
document.documentElement.lang = currentLang;
|
deBtn.classList.toggle("active", isDe);
|
||||||
updateLanguageUI();
|
enBtn.classList.toggle("active", !isDe);
|
||||||
if (typeof onLanguageChange === "function") {
|
}
|
||||||
onLanguageChange(currentLang);
|
|
||||||
}
|
const navQuiz = document.getElementById("navQuiz");
|
||||||
}
|
const navLeaderboard = document.getElementById("navLeaderboard");
|
||||||
|
const navSuggest = document.getElementById("navSuggest");
|
||||||
function updateLanguageUI() {
|
const navAdmin = document.getElementById("navAdmin");
|
||||||
const isDe = currentLang === "de";
|
const footerText = document.getElementById("footerText");
|
||||||
const deBtn = document.getElementById("langDe");
|
const footerImpressum = document.getElementById("footerImpressum");
|
||||||
const enBtn = document.getElementById("langEn");
|
const footerDatenschutz = document.getElementById("footerDatenschutz");
|
||||||
if (deBtn && enBtn) {
|
|
||||||
deBtn.classList.toggle("active", isDe);
|
if (navQuiz) navQuiz.innerText = isDe ? "🎯 Quiz" : "🎯 Quiz";
|
||||||
enBtn.classList.toggle("active", !isDe);
|
if (navLeaderboard) navLeaderboard.innerText = isDe ? "🏆 Bestenliste" : "🏆 Leaderboard";
|
||||||
}
|
if (navSuggest) navSuggest.innerText = isDe ? "✍️ Frage einreichen" : "✍️ Submit Question";
|
||||||
|
if (navAdmin) navAdmin.innerText = isDe ? "⚙️ Admin" : "⚙️ Admin";
|
||||||
const navQuiz = document.getElementById("navQuiz");
|
if (footerText) {
|
||||||
const navLeaderboard = document.getElementById("navLeaderboard");
|
footerText.innerText = isDe
|
||||||
const navSuggest = document.getElementById("navSuggest");
|
? "⚡ PotterTrivia · Das unabhängige Harry-Potter-Wissensquiz"
|
||||||
const navAdmin = document.getElementById("navAdmin");
|
: "⚡ PotterTrivia · The Independent Wizarding World Trivia Quiz";
|
||||||
const footerText = document.getElementById("footerText");
|
}
|
||||||
const footerImpressum = document.getElementById("footerImpressum");
|
if (footerImpressum) footerImpressum.innerText = isDe ? "⚖️ Impressum" : "⚖️ Legal Notice";
|
||||||
const footerDatenschutz = document.getElementById("footerDatenschutz");
|
if (footerDatenschutz) footerDatenschutz.innerText = isDe ? "🛡️ Datenschutz" : "🛡️ Privacy Policy";
|
||||||
|
}
|
||||||
if (navQuiz) navQuiz.innerText = isDe ? "✨ Zum Quiz" : "✨ To Quiz";
|
|
||||||
if (navLeaderboard) navLeaderboard.innerText = isDe ? "🏆 Bestenliste" : "🏆 Leaderboard";
|
function toggleMobileMenu() {
|
||||||
if (navSuggest) navSuggest.innerText = isDe ? "✍️ Frage einreichen" : "✍️ Submit Question";
|
const isOpen = navLinks.classList.toggle("nav-open");
|
||||||
if (navAdmin) navAdmin.innerText = isDe ? "🔒 Admin-Cockpit" : "🔒 Admin Cockpit";
|
menuIcon.innerText = isOpen ? "✕" : "☰";
|
||||||
if (footerText) {
|
}
|
||||||
footerText.innerText = isDe
|
|
||||||
? "⚡ Magisches Hogwarts-Escape-Quiz · Betrieben mit Liebe & Zauberkraft ⚡"
|
function closeMobileMenu() {
|
||||||
: "⚡ Magical Hogwarts Escape Quiz · Powered by Love & Wizardry ⚡";
|
navLinks.classList.remove("nav-open");
|
||||||
}
|
menuIcon.innerText = "☰";
|
||||||
if (footerImpressum) footerImpressum.innerText = isDe ? "⚖️ Impressum" : "⚖️ Legal Notice";
|
}
|
||||||
if (footerDatenschutz) footerDatenschutz.innerText = isDe ? "🛡️ Datenschutz" : "🛡️ Privacy Policy";
|
|
||||||
if (audioText) {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const isPlaying = bgm && !bgm.paused;
|
updateLanguageUI();
|
||||||
if (isDe) {
|
});
|
||||||
audioText.innerText = isPlaying ? "Musik: An" : "Musik: Aus";
|
</script>
|
||||||
} else {
|
{% block scripts %}{% endblock %}
|
||||||
audioText.innerText = isPlaying ? "Music: On" : "Music: Off";
|
</body>
|
||||||
}
|
</html>
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleAudio() {
|
|
||||||
const isDe = currentLang === "de";
|
|
||||||
if (bgm.paused) {
|
|
||||||
bgm.play().then(() => {
|
|
||||||
audioIcon.innerText = "🔊";
|
|
||||||
audioText.innerText = isDe ? "Musik: An" : "Music: On";
|
|
||||||
localStorage.setItem("bgm_enabled", "true");
|
|
||||||
}).catch(err => {
|
|
||||||
console.log("Audio play blocked:", err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
bgm.pause();
|
|
||||||
audioIcon.innerText = "🔇";
|
|
||||||
audioText.innerText = isDe ? "Musik: Aus" : "Music: Off";
|
|
||||||
localStorage.setItem("bgm_enabled", "false");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleMobileMenu() {
|
|
||||||
const isOpen = navLinks.classList.toggle("nav-open");
|
|
||||||
menuIcon.innerText = isOpen ? "✕" : "☰";
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeMobileMenu() {
|
|
||||||
navLinks.classList.remove("nav-open");
|
|
||||||
menuIcon.innerText = "☰";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-resume audio if previously enabled
|
|
||||||
document.addEventListener("click", function initAudioOnInteraction() {
|
|
||||||
if (localStorage.getItem("bgm_enabled") === "true" && bgm.paused) {
|
|
||||||
bgm.play().then(() => {
|
|
||||||
audioIcon.innerText = "🔊";
|
|
||||||
audioText.innerText = (currentLang === "de") ? "Musik: An" : "Music: On";
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
|
||||||
document.removeEventListener("click", initAudioOnInteraction);
|
|
||||||
}, { once: true });
|
|
||||||
|
|
||||||
// Initialize UI on load
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
|
||||||
updateLanguageUI();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% block scripts %}{% endblock %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
+160
-174
@@ -1,174 +1,160 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}✨ Magisches Harry-Potter-Escape-Quiz ✨{% endblock %}
|
{% block title %}⚡ PotterTrivia – Das Harry-Potter-Wissensquiz{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="quiz-wrapper">
|
<div class="quiz-wrapper">
|
||||||
<header class="quiz-header">
|
<header class="quiz-header">
|
||||||
<h1 class="glow-title" id="pageTitle">✨ Magisches Hogwarts-Escape-Quiz ✨</h1>
|
<div class="hero-pill" id="heroPill">⚡ {{ question_count }} Fragen im Wissens-Pool · Buch & Film</div>
|
||||||
<p class="subtitle" id="pageSubtitle">10 knifflige Rätsel aus der Zauberwelt warten auf dich</p>
|
<h1 class="glow-title" id="pageTitle">Harry Potter Wissensquiz</h1>
|
||||||
</header>
|
<p class="subtitle" id="pageSubtitle">Teste dein Wissen über Bücher, Filme, Geschöpfe und Zaubersprüche – direkt & ohne Umwege.</p>
|
||||||
|
</header>
|
||||||
<!-- Brief-Intro / Startmaske -->
|
|
||||||
<div id="introCard" class="card intro-card intro-parchment-wrapper">
|
<!-- Kompaktes Direct-Start Dashboard -->
|
||||||
<!-- Antiker geschlossener Hogwarts-Umschlag (öffnet sich bei Klick auf das Siegel) -->
|
<div id="introCard" class="card start-dashboard-card">
|
||||||
<div class="antique-envelope-scene" id="envelope">
|
<div class="dashboard-grid">
|
||||||
<div class="envelope-base">
|
<!-- Sektion 1: Schwierigkeitsgrad -->
|
||||||
<!-- Dunkleres Pergament-Innenfutter -->
|
<div class="config-section">
|
||||||
<div class="envelope-interior"></div>
|
<div class="config-header">
|
||||||
|
<span class="config-step">1</span>
|
||||||
<!-- Der darin liegende Briefbogen (gleitet beim Öffnen majestätisch hervor) -->
|
<span class="difficulty-label" id="diffLabel">Schwierigkeitsgrad</span>
|
||||||
<div class="letter-sheet" id="letterSheet">
|
</div>
|
||||||
<div class="sheet-parchment-layer"></div>
|
<div class="difficulty-chips" role="radiogroup" aria-label="Schwierigkeitsgrad">
|
||||||
<header class="letter-header">
|
<button type="button" class="diff-chip active" data-diff="all" onclick="selectDifficulty('all')">
|
||||||
<div class="letter-crest-box">
|
<span class="diff-icon">🎲</span>
|
||||||
<img src="/static/img/hogwarts_seal.jpg" alt="Hogwarts Crest" class="letter-crest-sketch">
|
<span class="diff-text" id="diffAllText">Alle Stufen</span>
|
||||||
</div>
|
</button>
|
||||||
<h3 class="letter-school-name" id="letterSchoolTitle">Hogwarts-Schule für Hexerei und Zauberei</h3>
|
<button type="button" class="diff-chip" data-diff="easy" onclick="selectDifficulty('easy')">
|
||||||
<p class="letter-headmaster" id="letterHeadmaster">Schulleiter: Albus Dumbledore<br><span>(Großhexenmeister, Träger des Merlin-Ordens 1. Klasse)</span></p>
|
<span class="diff-icon">🟢</span>
|
||||||
<div class="letter-ornament-line">
|
<span class="diff-text" id="diffEasyText">Leicht</span>
|
||||||
<span class="ornament-leaf">❦</span>
|
</button>
|
||||||
<span class="ornament-rule"></span>
|
<button type="button" class="diff-chip" data-diff="medium" onclick="selectDifficulty('medium')">
|
||||||
<span class="ornament-shield">✦</span>
|
<span class="diff-icon">🟡</span>
|
||||||
<span class="ornament-rule"></span>
|
<span class="diff-text" id="diffMedText">Mittel</span>
|
||||||
<span class="ornament-leaf">❧</span>
|
</button>
|
||||||
</div>
|
<button type="button" class="diff-chip" data-diff="hard" onclick="selectDifficulty('hard')">
|
||||||
</header>
|
<span class="diff-icon">🔴</span>
|
||||||
|
<span class="diff-text" id="diffHardText">Schwer (Experte)</span>
|
||||||
<div class="letter-body-handwritten">
|
</button>
|
||||||
<p class="letter-salutation" id="letterSalutation">Liebe Hexe, lieber Zauberer,</p>
|
</div>
|
||||||
<p class="letter-paragraph" id="letterParagraph1">
|
</div>
|
||||||
wir freuen uns, dir mitteilen zu dürfen, dass du zu den <strong>Magischen Z.A.G.-Prüfungen</strong> an der Hogwarts-Schule zugelassen bist.
|
|
||||||
</p>
|
<!-- Sektion 2: Kategorie-Filter -->
|
||||||
<p class="letter-paragraph" id="letterParagraph2">
|
<div class="config-section">
|
||||||
Zehn anspruchsvolle Prüfungsfragen entscheiden über deinen Rang in den Chroniken. Zeige deinen Scharfsinn und sichere bis zu <strong>1.000 Hauspunkte</strong> für dein Haus!
|
<div class="config-header">
|
||||||
</p>
|
<span class="config-step">2</span>
|
||||||
</div>
|
<span class="difficulty-label" id="catLabel">Themengebiet wählen</span>
|
||||||
|
</div>
|
||||||
<footer class="letter-signature-block">
|
<div class="category-chips" role="radiogroup" aria-label="Themengebiet">
|
||||||
<p class="letter-closing" id="letterClosing">Mit vorzüglicher Hochachtung,</p>
|
<button type="button" class="cat-chip active" data-cat="all" onclick="selectCategory('all')">
|
||||||
<p class="letter-signature-ink" id="letterSignature">Minerva McGonagall</p>
|
<span>✨</span> <span id="catAllText">Alle Kategorien</span>
|
||||||
<p class="letter-sign-title" id="letterSignTitle">Stellvertretende Schulleiterin</p>
|
</button>
|
||||||
</footer>
|
<button type="button" class="cat-chip" data-cat="books" onclick="selectCategory('books')">
|
||||||
</div>
|
<span>📖</span> <span id="catBooksText">Buchwissen</span>
|
||||||
|
</button>
|
||||||
<!-- Vordere Umschlagtasche mit handschriftlicher Smaragd-Anschrift -->
|
<button type="button" class="cat-chip" data-cat="movies" onclick="selectCategory('movies')">
|
||||||
<div class="envelope-front-pocket">
|
<span>🎬</span> <span id="catMoviesText">Filmwissen</span>
|
||||||
<div class="envelope-sketch-border"></div>
|
</button>
|
||||||
<div class="envelope-address-block">
|
<button type="button" class="cat-chip" data-cat="creatures" onclick="selectCategory('creatures')">
|
||||||
<p class="addr-line addr-school" id="envelopeLetter">Hogwarts-Schule für Hexerei und Zauberei</p>
|
<span>🐉</span> <span id="catCreaturesText">Magische Geschöpfe</span>
|
||||||
<p class="addr-line addr-recipient" id="envelopeRecipient">An den ehrenwerten Zauberschüler</p>
|
</button>
|
||||||
<p class="addr-line addr-location" id="envelopeLocation">Auf dem Prüfungspfad • Schloss Hogwarts</p>
|
<button type="button" class="cat-chip" data-cat="spells" onclick="selectCategory('spells')">
|
||||||
</div>
|
<span>🪄</span> <span id="catSpellsText">Zaubersprüche & Tränke</span>
|
||||||
</div>
|
</button>
|
||||||
|
<button type="button" class="cat-chip" data-cat="characters" onclick="selectCategory('characters')">
|
||||||
<!-- Obere Klappe des Umschlags (überdeckt die Tasche und wird vom Siegel gehalten) -->
|
<span>🧙♂️</span> <span id="catCharsText">Charaktere</span>
|
||||||
<div class="envelope-closing-flap">
|
</button>
|
||||||
<div class="flap-sketch-line"></div>
|
<button type="button" class="cat-chip" data-cat="hogwarts" onclick="selectCategory('hogwarts')">
|
||||||
</div>
|
<span>🏰</span> <span id="catHogwartsText">Hogwarts & Orte</span>
|
||||||
|
</button>
|
||||||
<!-- Handgezeichnetes Wachssiegel über der Klappenspitze -->
|
<button type="button" class="cat-chip" data-cat="quidditch" onclick="selectCategory('quidditch')">
|
||||||
<div class="sketched-wax-seal" id="waxSeal" onclick="startGame()" title="Siegel brechen & Brief öffnen">
|
<span>🧹</span> <span id="catQuidditchText">Quidditch</span>
|
||||||
<img src="/static/img/hogwarts_seal.jpg" alt="Hogwarts-Wappen Siegel" class="seal-crest-img">
|
</button>
|
||||||
<div class="seal-emboss-ring"></div>
|
<button type="button" class="cat-chip" data-cat="lore" onclick="selectCategory('lore')">
|
||||||
<div class="seal-glow"></div>
|
<span>🔮</span> <span id="catLoreText">Artefakte & Lore</span>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Auswahlbereich unter dem Pergament -->
|
|
||||||
<div class="intro-content intro-controls-panel">
|
<div class="dashboard-footer">
|
||||||
<p class="intro-subnote" id="introDesc">
|
<div class="dashboard-rules" id="dashboardRules">
|
||||||
Ein verzauberter Pergamentbrief aus Hogwarts liegt vor dir.<br>
|
<span>🎯 10 Fragen pro Runde</span>
|
||||||
Wähle deinen Schwierigkeitsgrad und brich das rote Zaubersiegel:
|
<span>·</span>
|
||||||
</p>
|
<span>🔥 Streak-Zähler</span>
|
||||||
|
<span>·</span>
|
||||||
<!-- Difficulty Selector Chips -->
|
<span>💡 Bis zu 3 Versuche je Frage</span>
|
||||||
<div class="difficulty-selection">
|
</div>
|
||||||
<span class="difficulty-label" id="diffLabel">Schwierigkeitsgrad wählen:</span>
|
<button id="startQuizBtn" class="btn btn-primary btn-large btn-launch-quiz" onclick="startGame()">
|
||||||
<div class="difficulty-chips" role="radiogroup" aria-label="Schwierigkeitsgrad">
|
⚡ Quiz jetzt starten ➔
|
||||||
<button type="button" class="diff-chip active" data-diff="all" onclick="selectDifficulty('all')">
|
</button>
|
||||||
<span class="diff-icon">🎲</span> <span class="diff-text" id="diffAllText">Alle gemischt</span>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
<button type="button" class="diff-chip" data-diff="easy" onclick="selectDifficulty('easy')">
|
|
||||||
<span class="diff-icon">🟢</span> <span class="diff-text" id="diffEasyText">Leicht</span>
|
<!-- Spielbereich (initial versteckt) -->
|
||||||
</button>
|
<div id="gameCard" class="game-area hidden">
|
||||||
<button type="button" class="diff-chip" data-diff="medium" onclick="selectDifficulty('medium')">
|
<!-- HUD: Fortschritt, Streak, Zeit & Punkte -->
|
||||||
<span class="diff-icon">🟡</span> <span class="diff-text" id="diffMedText">Mittel</span>
|
<div class="status-panel">
|
||||||
</button>
|
<div class="progress-info">
|
||||||
<button type="button" class="diff-chip" data-diff="hard" onclick="selectDifficulty('hard')">
|
<span id="questionCounter" class="hud-pill">Frage 1 / 10</span>
|
||||||
<span class="diff-icon">🔴</span> <span class="diff-text" id="diffHardText">Schwer</span>
|
<div class="hud-center-group">
|
||||||
</button>
|
<span id="streakBadge" class="streak-badge" title="Antwort-Serie in Folge">🔥 0</span>
|
||||||
</div>
|
<span id="timerBadge" class="timer-badge">⏱️ 00:00</span>
|
||||||
</div>
|
</div>
|
||||||
|
<span id="pointsCounter" class="points-badge">🏆 0 Pkt.</span>
|
||||||
<button id="startBtn" class="btn btn-primary btn-large btn-break-seal" onclick="startGame()">
|
</div>
|
||||||
📜 Siegel brechen & Brief öffnen
|
<div class="progress-container">
|
||||||
</button>
|
<div id="progressBar" class="progress-bar"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Spielbereich (initial versteckt) -->
|
<!-- Frage-Container -->
|
||||||
<div id="gameCard" class="game-area hidden">
|
<div id="frageBox" class="card question-card">
|
||||||
<!-- Fortschritt & Punkte & Zeit -->
|
<div class="question-meta">
|
||||||
<div class="status-panel">
|
<div class="meta-left">
|
||||||
<div class="progress-info">
|
<span id="badgeCategory" class="badge">⚡ Allgemein</span>
|
||||||
<span id="questionCounter">Frage 1 von 10</span>
|
<span id="badgeDifficulty" class="badge badge-diff">🟢 Leicht</span>
|
||||||
<span id="timerBadge" class="timer-badge">⏱️ 00:00</span>
|
</div>
|
||||||
<span id="pointsCounter" class="points-badge">🏆 Hauspunkte: 0</span>
|
<span id="attemptBadge" class="attempt-badge">Versuch 1 / 3</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress-container">
|
|
||||||
<div id="progressBar" class="progress-bar"></div>
|
<h2 id="frageText" class="question-text">Lade nächste Frage...</h2>
|
||||||
</div>
|
|
||||||
</div>
|
<!-- Freitext-Eingabe (wenn question_type == 'text') -->
|
||||||
|
<div id="textAnswerGroup" class="answer-form">
|
||||||
<!-- Frage-Container -->
|
<input type="text" id="answerInput" class="input-answer" placeholder="Deine Antwort eingeben..." autocomplete="off">
|
||||||
<div id="frageBox" class="card question-card">
|
|
||||||
<div class="question-meta">
|
<div class="btn-group">
|
||||||
<div class="meta-left">
|
<button id="checkBtn" class="btn btn-primary" onclick="checkAnswer()">
|
||||||
<span id="badgeCategory" class="badge">⚡ Harry Potter</span>
|
✓ Antwort prüfen
|
||||||
<span id="badgeDifficulty" class="badge badge-diff">🟢 Leicht</span>
|
</button>
|
||||||
</div>
|
<button id="hintBtn" class="btn btn-secondary" onclick="requestHint()">
|
||||||
<span id="attemptBadge" class="attempt-badge">Versuch 1 von 3</span>
|
💡 Tipp anzeigen
|
||||||
</div>
|
</button>
|
||||||
|
</div>
|
||||||
<h2 id="frageText" class="question-text">Lade verzauberte Schriftrollen...</h2>
|
</div>
|
||||||
|
|
||||||
<!-- Freitext-Eingabe (wenn question_type == 'text') -->
|
<!-- Multiple-Choice-Auswahl (wenn question_type == 'multiple_choice') -->
|
||||||
<div id="textAnswerGroup" class="answer-form">
|
<div id="choiceAnswerGroup" class="choice-group hidden">
|
||||||
<input type="text" id="answerInput" class="input-answer" placeholder="Deine Antwort hier eingeben..." autocomplete="off">
|
<div id="choiceOptionsContainer" class="choice-grid"></div>
|
||||||
|
<div class="btn-group" style="margin-top: 16px;">
|
||||||
<div class="btn-group">
|
<button id="hintBtnMC" class="btn btn-secondary" onclick="requestHint()">
|
||||||
<button id="checkBtn" class="btn btn-primary" onclick="checkAnswer()">
|
💡 Tipp anzeigen
|
||||||
✨ Antwort prüfen
|
</button>
|
||||||
</button>
|
</div>
|
||||||
<button id="hintBtn" class="btn btn-secondary" onclick="requestHint()">
|
</div>
|
||||||
💡 Hinweis anzeigen
|
|
||||||
</button>
|
<div id="hintBox" class="hint-box hidden">
|
||||||
</div>
|
<strong id="hintHeading">💡 Hinweis:</strong>
|
||||||
</div>
|
<p id="hintText"></p>
|
||||||
|
</div>
|
||||||
<!-- Multiple-Choice-Auswahl (wenn question_type == 'multiple_choice') -->
|
|
||||||
<div id="choiceAnswerGroup" class="choice-group hidden">
|
<div id="feedbackBox" class="feedback-box"></div>
|
||||||
<div id="choiceOptionsContainer" class="choice-grid"></div>
|
</div>
|
||||||
<div class="btn-group" style="margin-top: 15px;">
|
</div>
|
||||||
<button id="hintBtnMC" class="btn btn-secondary" onclick="requestHint()">
|
</div>
|
||||||
💡 Hinweis anzeigen
|
{% endblock %}
|
||||||
</button>
|
|
||||||
</div>
|
{% block scripts %}
|
||||||
</div>
|
<script src="/static/js/quiz.js?v=4.0"></script>
|
||||||
|
{% endblock %}
|
||||||
<div id="hintBox" class="hint-box hidden">
|
|
||||||
<strong id="hintHeading">📜 Notiz aus der verbotenen Abteilung:</strong>
|
|
||||||
<p id="hintText"></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="feedbackBox" class="feedback-box"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
<script src="/static/js/quiz.js?v=3.2"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="quiz-wrapper">
|
<div class="quiz-wrapper">
|
||||||
<div class="card vorschlag-card">
|
<div class="card vorschlag-card">
|
||||||
<h1 class="glow-title" id="suggTitle">✨ Neue Frage für das Quiz vorschlagen ✨</h1>
|
<h1 class="glow-title" id="suggTitle">✍️ Neue Quiz-Frage einreichen</h1>
|
||||||
<p class="subtitle" id="suggSubtitle">Bereichere das magische Quiz mit kniffligen Harry-Potter- oder Escape-Rätseln!</p>
|
<p class="subtitle" id="suggSubtitle">Bereichere PotterTrivia mit deinen eigenen Fragen zu Büchern, Filmen oder magischen Geschöpfen!</p>
|
||||||
|
|
||||||
<div class="info-alert" id="suggAlert">
|
<div class="info-alert" id="suggAlert">
|
||||||
ℹ️ <strong>Hinweis zur Moderation:</strong> Eingereichte Fragen werden nicht sofort öffentlich sichtbar, sondern erhalten automatisch den Status <em>„Prüfung ausstehend“</em>. Unser Zaubereiministerium prüft und testet die Antwort, bevor die Frage freigeschaltet wird.
|
ℹ️ <strong>Hinweis zur Moderation:</strong> Eingereichte Fragen werden kurz auf Richtigkeit geprüft, bevor sie im öffentlichen Quiz-Pool freigeschaltet werden.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="suggestionForm" class="magic-form" onsubmit="submitSuggestion(event)">
|
<form id="suggestionForm" class="magic-form" onsubmit="submitSuggestion(event)">
|
||||||
@@ -26,13 +26,15 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="kategorie" id="suggCatLabel">Kategorie / Themenbereich:</label>
|
<label for="kategorie" id="suggCatLabel">Kategorie / Themenbereich:</label>
|
||||||
<select id="kategorie" name="category" class="input-text">
|
<select id="kategorie" name="category" class="input-text">
|
||||||
<option value="hogwarts" id="catHogwarts">🏰 Hogwarts & Schulgelände</option>
|
<option value="books" id="catBooks">📖 Buchwissen (Buch-Details)</option>
|
||||||
<option value="characters" id="catCharacters" selected>🧙 Charaktere & Personen</option>
|
<option value="movies" id="catMovies">🎬 Filmwissen (Kino & Drehorte)</option>
|
||||||
<option value="spells" id="catSpells">⚡ Zaubersprüche & Magie</option>
|
<option value="creatures" id="catCreatures">🐉 Magische Geschöpfe</option>
|
||||||
<option value="creatures" id="catCreatures">🐉 Magische Tierwesen</option>
|
<option value="spells" id="catSpells">🪄 Zaubersprüche & Tränke</option>
|
||||||
|
<option value="characters" id="catCharacters" selected>🧙♂️ Charaktere & Personen</option>
|
||||||
|
<option value="hogwarts" id="catHogwarts">🏰 Hogwarts & Orte</option>
|
||||||
<option value="quidditch" id="catQuidditch">🧹 Quidditch & Sport</option>
|
<option value="quidditch" id="catQuidditch">🧹 Quidditch & Sport</option>
|
||||||
<option value="lore" id="catLore">🔮 Artefakte & Geschichte</option>
|
<option value="lore" id="catLore">🔮 Artefakte & Geschichte</option>
|
||||||
<option value="general" id="catGeneral">📜 Allgemeines Zauberwissen</option>
|
<option value="general" id="catGeneral">⚡ Allgemeines Wissen</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
annotated-doc==0.0.5
|
||||||
|
annotated-types==0.8.0
|
||||||
|
anyio==4.15.1
|
||||||
|
cffi==2.1.1
|
||||||
|
click==8.5.0
|
||||||
|
cryptography==50.0.1
|
||||||
|
fastapi==0.141.1
|
||||||
|
greenlet==3.5.6
|
||||||
|
h11==0.16.0
|
||||||
|
httptools==0.8.0
|
||||||
|
idna==3.20
|
||||||
|
itsdangerous==2.2.0
|
||||||
|
Jinja2==3.1.6
|
||||||
|
MarkupSafe==3.0.3
|
||||||
|
pycparser==3.0
|
||||||
|
pydantic==2.13.5
|
||||||
|
pydantic_core==2.46.5
|
||||||
|
PyMySQL==1.2.3
|
||||||
|
python-dotenv==1.2.3
|
||||||
|
python-multipart==0.0.32
|
||||||
|
PyYAML==6.0.3
|
||||||
|
SQLAlchemy==2.0.54
|
||||||
|
starlette==1.6.0
|
||||||
|
typing-inspection==0.4.4
|
||||||
|
typing_extensions==4.16.0
|
||||||
|
uvicorn==0.53.0
|
||||||
|
uvloop==0.22.1
|
||||||
|
watchfiles==1.3.0
|
||||||
|
websockets==17.1
|
||||||
Reference in New Issue
Block a user