193 lines
5.6 KiB
Python
193 lines
5.6 KiB
Python
import os
|
|
import random
|
|
import time
|
|
import psycopg2
|
|
from flask import Flask, render_template, request, jsonify
|
|
from difflib import SequenceMatcher
|
|
|
|
app = Flask(__name__)
|
|
|
|
# DB Verbindung mit Retry-Logik
|
|
def get_db_connection():
|
|
max_retries = 5
|
|
for i in range(max_retries):
|
|
try:
|
|
conn = psycopg2.connect(
|
|
host=os.environ.get('DB_HOST', 'postgres'),
|
|
database=os.environ.get('DB_NAME', 'englisch6a'),
|
|
user=os.environ.get('DB_USER', 'englisch6a'),
|
|
password=os.environ.get('DB_PASS', 'englisch')
|
|
)
|
|
return conn
|
|
except psycopg2.OperationalError:
|
|
if i < max_retries - 1:
|
|
time.sleep(2)
|
|
continue
|
|
else:
|
|
raise
|
|
|
|
def init_db_if_needed():
|
|
try:
|
|
conn = get_db_connection()
|
|
cur = conn.cursor()
|
|
|
|
# Prüfen ob Tabelle existiert
|
|
cur.execute("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'vocabulary');")
|
|
exists = cur.fetchone()[0]
|
|
|
|
if not exists:
|
|
print("Tabelle 'vocabulary' nicht gefunden. Initialisiere Datenbank aus init.sql...")
|
|
if os.path.exists('init.sql'):
|
|
with open('init.sql', 'r', encoding='utf-8') as f:
|
|
sql_commands = f.read()
|
|
cur.execute(sql_commands)
|
|
conn.commit()
|
|
print("Datenbank erfolgreich initialisiert!")
|
|
else:
|
|
print("Fehler: init.sql nicht gefunden!")
|
|
else:
|
|
# Optional: Prüfen ob leer
|
|
cur.execute("SELECT COUNT(*) FROM vocabulary")
|
|
count = cur.fetchone()[0]
|
|
if count == 0:
|
|
print("Tabelle leer. Fülle Daten...")
|
|
if os.path.exists('init.sql'):
|
|
with open('init.sql', 'r', encoding='utf-8') as f:
|
|
sql_commands = f.read()
|
|
cur.execute(sql_commands)
|
|
conn.commit()
|
|
|
|
cur.close()
|
|
conn.close()
|
|
except Exception as e:
|
|
print(f"Datenbank-Check Fehler: {e}")
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template('index.html')
|
|
|
|
@app.route('/api/pages')
|
|
def get_pages():
|
|
conn = get_db_connection()
|
|
cur = conn.cursor()
|
|
cur.execute('SELECT DISTINCT page FROM vocabulary ORDER BY page')
|
|
pages = [row[0] for row in cur.fetchall()]
|
|
cur.close()
|
|
conn.close()
|
|
return jsonify(pages)
|
|
|
|
@app.route('/api/start', methods=['POST'])
|
|
def start_quiz():
|
|
data = request.json
|
|
pages = data.get('pages', [])
|
|
mode = data.get('mode', 'random')
|
|
|
|
conn = get_db_connection()
|
|
cur = conn.cursor()
|
|
|
|
if not pages:
|
|
cur.execute('SELECT id, english, german FROM vocabulary')
|
|
else:
|
|
cur.execute('SELECT id, english, german FROM vocabulary WHERE page = ANY(%s)', (pages,))
|
|
|
|
rows = cur.fetchall()
|
|
cur.close()
|
|
conn.close()
|
|
|
|
vocab_list = []
|
|
for row in rows:
|
|
vid, eng, ger = row
|
|
|
|
direction = mode
|
|
if mode == 'random':
|
|
direction = random.choice(['de-en', 'en-de'])
|
|
|
|
question = ""
|
|
answer = ""
|
|
|
|
if direction == 'de-en':
|
|
question = ger
|
|
answer = eng
|
|
else:
|
|
question = eng
|
|
answer = ger
|
|
|
|
vocab_list.append({
|
|
'id': vid,
|
|
'question': question,
|
|
'answer': answer,
|
|
'direction': direction
|
|
})
|
|
|
|
random.shuffle(vocab_list)
|
|
return jsonify(vocab_list)
|
|
|
|
def expand_variants(text):
|
|
segments = [s.strip() for s in text.split(';')]
|
|
possibilities = []
|
|
|
|
for seg in segments:
|
|
if '/-' in seg:
|
|
parts = seg.split('/-')
|
|
base = parts[0].strip()
|
|
suffixes = parts[1:]
|
|
possibilities.append(base)
|
|
for suf in suffixes:
|
|
clean_suf = suf.strip()
|
|
if clean_suf:
|
|
possibilities.append(base + clean_suf)
|
|
elif '/' in seg:
|
|
choices = seg.split('/')
|
|
possibilities.extend([c.strip() for c in choices])
|
|
else:
|
|
possibilities.append(seg)
|
|
|
|
return possibilities
|
|
|
|
def clean_text_for_comparison(text):
|
|
text = text.lower().strip()
|
|
if text.startswith('to '):
|
|
text = text[3:].strip()
|
|
return text
|
|
|
|
@app.route('/api/check', methods=['POST'])
|
|
def check_answer():
|
|
data = request.json
|
|
user_input = data.get('input', '')
|
|
db_answer = data.get('correct', '')
|
|
|
|
clean_user = clean_text_for_comparison(user_input)
|
|
valid_answers_raw = expand_variants(db_answer)
|
|
valid_answers_clean = [clean_text_for_comparison(v) for v in valid_answers_raw]
|
|
|
|
if clean_user in valid_answers_clean:
|
|
return jsonify({'status': 'correct', 'msg': 'Richtig!'})
|
|
|
|
best_ratio = 0
|
|
best_match = ""
|
|
for correct_option in valid_answers_clean:
|
|
ratio = SequenceMatcher(None, clean_user, correct_option).ratio()
|
|
if ratio > best_ratio:
|
|
best_ratio = ratio
|
|
best_match = correct_option
|
|
|
|
threshold = 0.75
|
|
if len(best_match) <= 4:
|
|
threshold = 0.8
|
|
|
|
if best_ratio >= threshold:
|
|
return jsonify({
|
|
'status': 'typo',
|
|
'msg': f'Fast richtig! Meintest du: "{best_match}"? (Es war "{user_input}")'
|
|
})
|
|
|
|
return jsonify({
|
|
'status': 'wrong',
|
|
'msg': f'Leider falsch. Versuche es erneut.'
|
|
})
|
|
|
|
if __name__ == '__main__':
|
|
# Beim Start DB prüfen und ggf. befüllen
|
|
init_db_if_needed()
|
|
app.run(host='0.0.0.0', port=5000)
|