From 090ccc98926b95b311570b3c81431214db73ae2e Mon Sep 17 00:00:00 2001 From: sascha Date: Wed, 28 Jan 2026 19:30:07 +0000 Subject: [PATCH] app/app.py aktualisiert --- app/app.py | 116 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 38 deletions(-) diff --git a/app/app.py b/app/app.py index 47c21d1..d98c4ac 100644 --- a/app/app.py +++ b/app/app.py @@ -2,11 +2,17 @@ import os import random import time import psycopg2 -from flask import Flask, render_template, request, jsonify +from flask import Flask, render_template, request, jsonify, session from difflib import SequenceMatcher app = Flask(__name__) +# Sessions Cookies +app.secret_key = 'super-secret-key-for-english-trainer' + +# Wie viele der letzten Wörter sollen pro Nutzer blockiert werden? +HISTORY_LIMIT = 15 + # --- DB VERBINDUNG & RESET --- def get_db_connection(): max_retries = 10 @@ -43,32 +49,35 @@ reset_db_on_startup() # --- HILFSFUNKTIONEN --- def clean_text(text): - """Basis-Reinigung für Verben-Input""" return text.lower().strip() def clean_vocab_input(text): - """ - Spezielle Reinigung für Vokabeln: - 1. Kleinbuchstaben & Leerzeichen weg. - 2. Führendes 'to ' entfernen (damit 'go' == 'to go'). - 3. Klammern entfernen (z.B. '(to)' -> weg). - """ t = text.lower().strip() - - # Klammern entfernen t = t.replace('(', '').replace(')', '').replace('[', '').replace(']', '') t = t.strip() - - # Führendes "to " entfernen (nur wenn es ein ganzes Wort ist) if t.startswith('to '): t = t[3:].strip() - return t def check_part(user_in, correct_str): variants = [x.strip().lower() for x in correct_str.split('/')] return user_in in variants +def update_user_history(word): + """Speichert das Wort im Cookie des Nutzers""" + # History aus Cookie laden (oder leere Liste) + history = session.get('history', []) + + # Wort hinzufügen + history.append(word) + + # Begrenzen auf die letzten X + if len(history) > HISTORY_LIMIT: + history = history[-HISTORY_LIMIT:] + + # Zurück in den Cookie speichern + session['history'] = history + # --- API ROUTEN --- @app.route('/') @@ -102,15 +111,39 @@ def get_question(): conn = get_db_connection() cur = conn.cursor() + + # Persönliche History aus dem Cookie holen + user_history = session.get('history', []) + + row = None + # --- LOGIK FÜR UNREGELMÄSSIGE VERBEN --- if main_mode == 'irregular': - query = "SELECT infinitive, simple_past, past_participle, german, page FROM irregular_verbs WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1" - cur.execute(query, (pages,)) + # Wir bauen den Query dynamisch: Schließe Wörter aus History aus + if user_history: + query_smart = "SELECT infinitive, simple_past, past_participle, german, page FROM irregular_verbs WHERE page = ANY(%s) AND infinitive NOT IN %s ORDER BY RANDOM() LIMIT 1" + cur.execute(query_smart, (pages, tuple(user_history))) + else: + query_smart = "SELECT infinitive, simple_past, past_participle, german, page FROM irregular_verbs WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1" + cur.execute(query_smart, (pages,)) + row = cur.fetchone() - cur.close() - conn.close() - if not row: return jsonify({'error': 'Keine Verben gefunden.'}) + # Fallback: Wenn durch History keine Wörter mehr übrig sind -> Reset (Zufall aus allem) + if not row: + print("User History voll/blockiert alles -> Fallback!") + query_fallback = "SELECT infinitive, simple_past, past_participle, german, page FROM irregular_verbs WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1" + cur.execute(query_fallback, (pages,)) + row = cur.fetchone() + + if not row: + cur.close(); conn.close() + return jsonify({'error': 'Keine Verben gefunden.'}) + + # Speichern im Cookie (Infinitive) + update_user_history(row[0]) + + cur.close(); conn.close() if sub_mode == 'start-german': return jsonify({ @@ -131,14 +164,32 @@ def get_question(): 'page': row[4] }) + # --- LOGIK FÜR NORMALE VOKABELN --- else: - query = "SELECT english, german, page FROM vocabulary WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1" - cur.execute(query, (pages,)) + # Auch hier: Exclude History + if user_history: + query_smart = "SELECT english, german, page FROM vocabulary WHERE page = ANY(%s) AND english NOT IN %s ORDER BY RANDOM() LIMIT 1" + cur.execute(query_smart, (pages, tuple(user_history))) + else: + query_smart = "SELECT english, german, page FROM vocabulary WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1" + cur.execute(query_smart, (pages,)) + row = cur.fetchone() - cur.close() - conn.close() - if not row: return jsonify({'error': 'Keine Vokabeln gefunden.'}) + # Fallback + if not row: + query_fallback = "SELECT english, german, page FROM vocabulary WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1" + cur.execute(query_fallback, (pages,)) + row = cur.fetchone() + + if not row: + cur.close(); conn.close() + return jsonify({'error': 'Keine Vokabeln gefunden.'}) + + # Speichern im Cookie (Englisch als ID) + update_user_history(row[0]) + + cur.close(); conn.close() q_text, a_text = "", "" if sub_mode == 'de-en': @@ -161,11 +212,10 @@ def check_answer(): data = request.json q_type = data.get('type') - # --- 1. & 2. VERBEN (Keine Typos, strikt) --- + # --- VERBEN --- if q_type.startswith('irregular'): is_correct = False - msg = "" - + if q_type == 'irregular_full': u_inf = clean_text(data.get('infinitive', '')) u_simp = clean_text(data.get('simple', '')) @@ -189,40 +239,31 @@ def check_answer(): if is_correct: return jsonify({'status': 'correct', 'msg': 'Richtig!'}) else: - # Bei Verben schicken wir KEINE Lösung im msg Feld, das macht das Frontend erst am Ende return jsonify({'status': 'wrong', 'msg': 'Leider nicht ganz richtig.'}) - # --- 3. VOKABELN (Mit "to"-Logik und Typos) --- + # --- VOKABELN --- else: u_input_raw = data.get('input', '') correct_raw = data.get('correct', '') - # Bereinigter Vergleich (ohne "to ", ohne Klammern) u_clean = clean_vocab_input(u_input_raw) - # Antwortmöglichkeiten vorbereiten (Splitten bei ';') valid_answers = [] raw_parts = correct_raw.split(';') for p in raw_parts: - # Original rein valid_answers.append(clean_vocab_input(p)) - # Falls "(to) go" drin stand -> das wird durch clean_vocab_input schon zu "go" - # Aber falls "to go" drin stand -> wird zu "go" - # 1. Direkter Treffer if u_clean in valid_answers: return jsonify({'status': 'correct', 'msg': 'Richtig!'}) - # 2. Fuzzy Match (Rechtschreibprüfung) best_ratio = 0 best_match = "" for v in valid_answers: ratio = SequenceMatcher(None, u_clean, v).ratio() if ratio > best_ratio: best_ratio = ratio - best_match = v # Das bereinigte Wort als Vorschlag + best_match = v - # Toleranz: 85% Übereinstimmung, bei kurzen Wörtern (<5 Zeichen) strenger (90%) threshold = 0.85 if len(best_match) < 5: threshold = 0.9 @@ -233,7 +274,6 @@ def check_answer(): 'msg': f'Fast richtig! Meintest du: "{best_match}"? (Achte auf die Schreibweise)' }) - # 3. Falsch return jsonify({'status': 'wrong', 'msg': 'Leider falsch.'}) if __name__ == '__main__':