From 95bbe139183121d018f61179d6203c62bb2c3612 Mon Sep 17 00:00:00 2001 From: sascha Date: Wed, 28 Jan 2026 15:37:05 +0000 Subject: [PATCH] app/app.py aktualisiert --- app/app.py | 270 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 154 insertions(+), 116 deletions(-) diff --git a/app/app.py b/app/app.py index 07fed2c..2ea03c7 100644 --- a/app/app.py +++ b/app/app.py @@ -7,7 +7,6 @@ from difflib import SequenceMatcher app = Flask(__name__) - # DB Verbindung mit Retry-Logik def get_db_connection(): max_retries = 5 @@ -27,41 +26,61 @@ def get_db_connection(): else: raise +# Erweiterte Init-Funktion: Checkt auf BEIDE Tabellen def init_db_if_needed(): try: conn = get_db_connection() 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');") - exists = cur.fetchone()[0] + vocab_exists = cur.fetchone()[0] - if not exists: - print("Tabelle 'vocabulary' nicht gefunden. Initialisiere Datenbank aus init.sql...") + # Check ob NEUE Verben-Tabelle da ist + 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'): 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() + print("Datenbank initialisiert.") cur.close() conn.close() 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('/') def index(): @@ -71,123 +90,142 @@ def index(): 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.execute("SELECT DISTINCT page FROM vocabulary ORDER BY page") + rows = cur.fetchall() cur.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) -@app.route('/api/start', methods=['POST']) -def start_quiz(): +@app.route('/api/question', methods=['POST']) +def get_question(): data = request.json + mode = data.get('mode', 'random') 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,)) + # --- NEU: UNREGELMÄSSIGE VERBEN MODUS --- + if 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,)) + row = cur.fetchone() + cur.close() + conn.close() - 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']) + if not row: + return jsonify({'error': 'Keine Verben für diese Seiten gefunden.'}) - 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 + return jsonify({ + 'type': 'irregular', + 'question': row[0], # Infinitive + 'german_hint': row[3], + 'answer_simple': row[1], + 'answer_participle': row[2], + 'page': row[4] }) + + # --- 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) - 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 + if not row: + return jsonify({'error': 'Keine Vokabeln gefunden.'}) + + question_text = "" + answer_text = "" + + if mode == 'de-en': + question_text = row[1] # Deutsch + answer_text = row[0] # Englisch + elif mode == 'en-de': + question_text = row[0] # Englisch + answer_text = row[1] # Deutsch + else: # random + if random.random() > 0.5: + question_text = row[1] + answer_text = row[0] + else: + question_text = row[0] + answer_text = row[1] + + return jsonify({ + 'type': 'vocab', + 'question': question_text, + 'answer': answer_text, + 'page': row[2] + }) @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!'}) + # --- NEU: CHECK FÜR VERBEN --- + if data.get('type') == 'irregular': + user_simple = clean_text_for_comparison(data.get('input_simple', '')) + user_participle = clean_text_for_comparison(data.get('input_participle', '')) - 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 + correct_simple = data.get('correct_simple', '') + correct_participle = data.get('correct_participle', '') + + # Wir müssen "was/were" oder "burnt/burned" handhaben -> split am slash + def check_part(user_in, correct_str): + variants = [x.strip().lower() for x in correct_str.split('/')] + 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 - if len(best_match) <= 4: - threshold = 0.8 + # Fuzzy Logic (Typo Check) + 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.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': 'typo', - 'msg': f'Fast richtig! Meintest du: "{best_match}"? (Es war "{user_input}")' - }) - - return jsonify({ - 'status': 'wrong', - 'msg': f'Leider falsch. Versuche es erneut.' - }) + return jsonify({'status': 'wrong', 'msg': f'Falsch. Lösung: {db_answer}'}) 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) + app.run(host='0.0.0.0', port=5000) \ No newline at end of file