From 8f1aa4e77f4717af6a27d04e35ff4439eac4959c Mon Sep 17 00:00:00 2001 From: sascha Date: Wed, 28 Jan 2026 18:51:00 +0000 Subject: [PATCH] app/app.py aktualisiert --- app/app.py | 138 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 52 deletions(-) diff --git a/app/app.py b/app/app.py index f0be7b4..47c21d1 100644 --- a/app/app.py +++ b/app/app.py @@ -7,7 +7,7 @@ from difflib import SequenceMatcher app = Flask(__name__) -# --- DB VERBINDUNG & RESET (Wie gehabt) --- +# --- DB VERBINDUNG & RESET --- def get_db_connection(): max_retries = 10 for i in range(max_retries): @@ -41,9 +41,30 @@ def reset_db_on_startup(): 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 @@ -56,9 +77,7 @@ def index(): @app.route('/api/pages') def get_pages(): - # Wir filtern jetzt nach Typ! - mode_type = request.args.get('type', 'vocab') # 'vocab' oder 'irregular' - + mode_type = request.args.get('type', 'vocab') conn = get_db_connection() cur = conn.cursor() @@ -77,15 +96,14 @@ def get_pages(): @app.route('/api/question', methods=['POST']) def get_question(): data = request.json - main_mode = data.get('mainMode') # 'vocab' oder 'irregular' - sub_mode = data.get('subMode') # 'de-en', 'start-german', etc. + main_mode = data.get('mainMode') + sub_mode = data.get('subMode') pages = data.get('pages', []) conn = get_db_connection() cur = conn.cursor() if main_mode == 'irregular': - # Verben Logik 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,)) row = cur.fetchone() @@ -94,22 +112,19 @@ def get_question(): if not row: return jsonify({'error': 'Keine Verben gefunden.'}) - # Entscheiden basierend auf sub_mode if sub_mode == 'start-german': - # Frage: Deutsch -> Antwort: Alle 3 Formen return jsonify({ 'type': 'irregular_full', - 'question': row[3], # Deutsch + 'question': row[3], 'answer_infinitive': row[0], 'answer_simple': row[1], 'answer_participle': row[2], 'page': row[4] }) else: - # Frage: Infinitiv -> Antwort: Past & Participle (Standard) return jsonify({ 'type': 'irregular_standard', - 'question': row[0], # Infinitive + 'question': row[0], 'german_hint': row[3], 'answer_simple': row[1], 'answer_participle': row[2], @@ -117,7 +132,6 @@ def get_question(): }) else: - # Vokabel Logik query = "SELECT english, german, page FROM vocabulary WHERE page = ANY(%s) ORDER BY RANDOM() LIMIT 1" cur.execute(query, (pages,)) row = cur.fetchone() @@ -131,7 +145,7 @@ def get_question(): q_text, a_text = row[1], row[0] elif sub_mode == 'en-de': q_text, a_text = row[0], row[1] - else: # random + else: if random.random() > 0.5: q_text, a_text = row[1], row[0] else: q_text, a_text = row[0], row[1] @@ -147,60 +161,80 @@ def check_answer(): data = request.json q_type = data.get('type') - # 1. Verben: Alles abfragen (Start: Deutsch) - if q_type == 'irregular_full': - u_inf = clean_text(data.get('infinitive', '')) - u_simp = clean_text(data.get('simple', '')) - u_part = clean_text(data.get('participle', '')) - - ok_inf = check_part(u_inf, data.get('correct_infinitive', '')) - ok_simp = check_part(u_simp, data.get('correct_simple', '')) - ok_part = check_part(u_part, data.get('correct_participle', '')) + # --- 1. & 2. VERBEN (Keine Typos, strikt) --- + if q_type.startswith('irregular'): + is_correct = False + msg = "" - if ok_inf and ok_simp and ok_part: - return jsonify({'status': 'correct', 'msg': 'Perfekt! Alles richtig.'}) - else: - sol = f"{data['correct_infinitive']} -> {data['correct_simple']} -> {data['correct_participle']}" - return jsonify({'status': 'wrong', 'msg': f'Leider falsch. Lösung: {sol}'}) + if q_type == 'irregular_full': + u_inf = clean_text(data.get('infinitive', '')) + u_simp = clean_text(data.get('simple', '')) + u_part = clean_text(data.get('participle', '')) + + ok_inf = check_part(u_inf, data.get('correct_infinitive', '')) + ok_simp = check_part(u_simp, data.get('correct_simple', '')) + ok_part = check_part(u_part, data.get('correct_participle', '')) + + if ok_inf and ok_simp and ok_part: is_correct = True - # 2. Verben: Standard (Start: Infinitiv) - elif q_type == 'irregular_standard': - u_simp = clean_text(data.get('simple', '')) - u_part = clean_text(data.get('participle', '')) - - ok_simp = check_part(u_simp, data.get('correct_simple', '')) - ok_part = check_part(u_part, data.get('correct_participle', '')) + elif q_type == 'irregular_standard': + u_simp = clean_text(data.get('simple', '')) + u_part = clean_text(data.get('participle', '')) + + ok_simp = check_part(u_simp, data.get('correct_simple', '')) + ok_part = check_part(u_part, data.get('correct_participle', '')) + + if ok_simp and ok_part: is_correct = True - if ok_simp and ok_part: + if is_correct: return jsonify({'status': 'correct', 'msg': 'Richtig!'}) else: - sol = f"{data['correct_simple']} -> {data['correct_participle']}" - return jsonify({'status': 'wrong', 'msg': f'Lösung: {sol}'}) + # 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 + # --- 3. VOKABELN (Mit "to"-Logik und Typos) --- else: - u_input = clean_text(data.get('input', '')) + u_input_raw = data.get('input', '') correct_raw = data.get('correct', '') - # Split variants (e.g. "car; auto") - valid_answers = [clean_text(x) for x in correct_raw.split(';')] - # Add version without brackets "(to) go" -> "go" - for v in list(valid_answers): - if '(' in v: valid_answers.append(v.replace('(','').replace(')','').strip()) + # 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" - if u_input in valid_answers: + # 1. Direkter Treffer + if u_clean in valid_answers: return jsonify({'status': 'correct', 'msg': 'Richtig!'}) - # Fuzzy Check + # 2. Fuzzy Match (Rechtschreibprüfung) best_ratio = 0 + best_match = "" for v in valid_answers: - ratio = SequenceMatcher(None, u_input, v).ratio() - if ratio > best_ratio: best_ratio = ratio + ratio = SequenceMatcher(None, u_clean, v).ratio() + if ratio > best_ratio: + best_ratio = ratio + best_match = v # Das bereinigte Wort als Vorschlag - if best_ratio >= 0.8: - return jsonify({'status': 'typo', 'msg': f'Fast richtig! Lösung: {correct_raw}'}) + # Toleranz: 85% Übereinstimmung, bei kurzen Wörtern (<5 Zeichen) strenger (90%) + threshold = 0.85 + if len(best_match) < 5: + threshold = 0.9 + + if best_ratio >= threshold: + return jsonify({ + 'status': 'typo', + 'msg': f'Fast richtig! Meintest du: "{best_match}"? (Achte auf die Schreibweise)' + }) - return jsonify({'status': 'wrong', 'msg': f'Falsch. Lösung: {correct_raw}'}) + # 3. Falsch + return jsonify({'status': 'wrong', 'msg': 'Leider falsch.'}) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000) \ No newline at end of file