app/app.py aktualisiert

This commit is contained in:
sascha 2026-01-28 15:37:05 +00:00
parent 1e53506f56
commit 95bbe13918

View File

@ -7,7 +7,6 @@ from difflib import SequenceMatcher
app = Flask(__name__) app = Flask(__name__)
# DB Verbindung mit Retry-Logik # DB Verbindung mit Retry-Logik
def get_db_connection(): def get_db_connection():
max_retries = 5 max_retries = 5
@ -27,41 +26,61 @@ def get_db_connection():
else: else:
raise raise
# Erweiterte Init-Funktion: Checkt auf BEIDE Tabellen
def init_db_if_needed(): def init_db_if_needed():
try: try:
conn = get_db_connection() conn = get_db_connection()
cur = conn.cursor() cur = conn.cursor()
# Prüfen ob Tabelle existiert # Check ob alte Vokabeltabelle da ist
cur.execute("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'vocabulary');") cur.execute("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'vocabulary');")
exists = cur.fetchone()[0] vocab_exists = cur.fetchone()[0]
if not exists: # Check ob NEUE Verben-Tabelle da ist
print("Tabelle 'vocabulary' nicht gefunden. Initialisiere Datenbank aus init.sql...") cur.execute("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'irregular_verbs');")
verbs_exists = cur.fetchone()[0]
# Wenn eine fehlt, führen wir init.sql aus (dank "CREATE TABLE IF NOT EXISTS" ist das sicher)
if not vocab_exists or not verbs_exists:
print("Datenbank unvollständig. Initialisiere aus init.sql...")
if os.path.exists('init.sql'): if os.path.exists('init.sql'):
with open('init.sql', 'r', encoding='utf-8') as f: with open('init.sql', 'r', encoding='utf-8') as f:
sql_commands = f.read() sql_commands = f.read()
cur.execute(sql_commands) cur.execute(sql_commands)
conn.commit() conn.commit()
print("Datenbank erfolgreich initialisiert!") print("Datenbank 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() cur.close()
conn.close() conn.close()
except Exception as e: except Exception as e:
print(f"Datenbank-Check Fehler: {e}") print(f"Fehler bei DB-Init: {e}")
# Beim Start einmal ausführen
init_db_if_needed()
def expand_variants(text):
""" Zerlegt 'Wort1; Wort2' in eine Liste """
parts = text.split(';')
results = []
for p in parts:
clean = p.strip()
if clean:
results.append(clean)
# Umgang mit Klammern z.B. "(to) go" -> "to go" und "go"
final_res = []
for r in results:
final_res.append(r)
if '(' in r:
without_parens = r.replace('(', '').replace(')', '').strip()
final_res.append(without_parens)
# Optional: auch Version ganz ohne Inhalt der Klammer?
# Hier einfachheitshalber: Klammern weg ist die Variante.
return final_res
def clean_text_for_comparison(text):
return text.lower().strip()
@app.route('/') @app.route('/')
def index(): def index():
@ -71,123 +90,142 @@ def index():
def get_pages(): def get_pages():
conn = get_db_connection() conn = get_db_connection()
cur = conn.cursor() cur = conn.cursor()
cur.execute('SELECT DISTINCT page FROM vocabulary ORDER BY page') cur.execute("SELECT DISTINCT page FROM vocabulary ORDER BY page")
pages = [row[0] for row in cur.fetchall()] rows = cur.fetchall()
cur.close() cur.close()
conn.close() conn.close()
pages = [r[0] for r in rows]
# Füge die Verben-Seiten manuell hinzu, falls noch nicht in vocabulary
for p in [206, 207]:
if p not in pages:
pages.append(p)
pages.sort()
return jsonify(pages) return jsonify(pages)
@app.route('/api/start', methods=['POST']) @app.route('/api/question', methods=['POST'])
def start_quiz(): def get_question():
data = request.json data = request.json
mode = data.get('mode', 'random')
pages = data.get('pages', []) pages = data.get('pages', [])
mode = data.get('mode', 'random')
conn = get_db_connection() conn = get_db_connection()
cur = conn.cursor() cur = conn.cursor()
if not pages: # --- NEU: UNREGELMÄSSIGE VERBEN MODUS ---
cur.execute('SELECT id, english, german FROM vocabulary') if mode == 'irregular':
else: query = "SELECT infinitive, simple_past, past_participle, german, page FROM irregular_verbs WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1"
cur.execute('SELECT id, english, german FROM vocabulary WHERE page = ANY(%s)', (pages,)) cur.execute(query, (pages,))
row = cur.fetchone()
cur.close()
conn.close()
rows = cur.fetchall() if not row:
cur.close() return jsonify({'error': 'Keine Verben für diese Seiten gefunden.'})
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 = "" return jsonify({
answer = "" 'type': 'irregular',
'question': row[0], # Infinitive
if direction == 'de-en': 'german_hint': row[3],
question = ger 'answer_simple': row[1],
answer = eng 'answer_participle': row[2],
else: 'page': row[4]
question = eng
answer = ger
vocab_list.append({
'id': vid,
'question': question,
'answer': answer,
'direction': direction
}) })
# --- ALTER MODUS (Vokabeln) ---
else:
query = "SELECT english, german, page FROM vocabulary WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1"
cur.execute(query, (pages,))
row = cur.fetchone()
cur.close()
conn.close()
random.shuffle(vocab_list) if not row:
return jsonify(vocab_list) return jsonify({'error': 'Keine Vokabeln gefunden.'})
def expand_variants(text): question_text = ""
segments = [s.strip() for s in text.split(';')] answer_text = ""
possibilities = []
if mode == 'de-en':
for seg in segments: question_text = row[1] # Deutsch
if '/-' in seg: answer_text = row[0] # Englisch
parts = seg.split('/-') elif mode == 'en-de':
base = parts[0].strip() question_text = row[0] # Englisch
suffixes = parts[1:] answer_text = row[1] # Deutsch
possibilities.append(base) else: # random
for suf in suffixes: if random.random() > 0.5:
clean_suf = suf.strip() question_text = row[1]
if clean_suf: answer_text = row[0]
possibilities.append(base + clean_suf) else:
elif '/' in seg: question_text = row[0]
choices = seg.split('/') answer_text = row[1]
possibilities.extend([c.strip() for c in choices])
else: return jsonify({
possibilities.append(seg) 'type': 'vocab',
'question': question_text,
return possibilities 'answer': answer_text,
'page': row[2]
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']) @app.route('/api/check', methods=['POST'])
def check_answer(): def check_answer():
data = request.json data = request.json
user_input = data.get('input', '')
db_answer = data.get('correct', '')
clean_user = clean_text_for_comparison(user_input) # --- NEU: CHECK FÜR VERBEN ---
valid_answers_raw = expand_variants(db_answer) if data.get('type') == 'irregular':
valid_answers_clean = [clean_text_for_comparison(v) for v in valid_answers_raw] user_simple = clean_text_for_comparison(data.get('input_simple', ''))
user_participle = clean_text_for_comparison(data.get('input_participle', ''))
if clean_user in valid_answers_clean:
return jsonify({'status': 'correct', 'msg': 'Richtig!'})
best_ratio = 0 correct_simple = data.get('correct_simple', '')
best_match = "" correct_participle = data.get('correct_participle', '')
for correct_option in valid_answers_clean:
ratio = SequenceMatcher(None, clean_user, correct_option).ratio() # Wir müssen "was/were" oder "burnt/burned" handhaben -> split am slash
if ratio > best_ratio: def check_part(user_in, correct_str):
best_ratio = ratio variants = [x.strip().lower() for x in correct_str.split('/')]
best_match = correct_option return user_in in variants
is_simple_ok = check_part(user_simple, correct_simple)
is_participle_ok = check_part(user_participle, correct_participle)
if is_simple_ok and is_participle_ok:
return jsonify({'status': 'correct', 'msg': 'Perfekt! Beide Formen richtig.'})
else:
return jsonify({
'status': 'wrong',
'msg': f'Leider falsch. Lösung: {correct_simple} | {correct_participle}'
})
# --- ALTER CHECK (Vokabeln) ---
else:
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!'})
threshold = 0.75 # Fuzzy Logic (Typo Check)
if len(best_match) <= 4: best_ratio = 0
threshold = 0.8 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.85
if len(best_match) <= 4:
threshold = 0.9
if best_ratio >= threshold:
return jsonify({
'status': 'typo',
'msg': f'Fast richtig! Meintest du: "{best_match}"? (Es war "{user_input}")'
})
if best_ratio >= threshold: return jsonify({'status': 'wrong', 'msg': f'Falsch. Lösung: {db_answer}'})
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__': if __name__ == '__main__':
# Beim Start DB prüfen und ggf. befüllen app.run(host='0.0.0.0', port=5000)
init_db_if_needed()
app.run(host='0.0.0.0', port=5000)