app/app.py aktualisiert
This commit is contained in:
parent
1e53506f56
commit
95bbe13918
256
app/app.py
256
app/app.py
@ -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
|
||||||
pages = data.get('pages', [])
|
|
||||||
mode = data.get('mode', 'random')
|
mode = data.get('mode', 'random')
|
||||||
|
pages = data.get('pages', [])
|
||||||
|
|
||||||
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 = []
|
return jsonify({
|
||||||
for row in rows:
|
'type': 'irregular',
|
||||||
vid, eng, ger = row
|
'question': row[0], # Infinitive
|
||||||
|
'german_hint': row[3],
|
||||||
direction = mode
|
'answer_simple': row[1],
|
||||||
if mode == 'random':
|
'answer_participle': row[2],
|
||||||
direction = random.choice(['de-en', 'en-de'])
|
'page': row[4]
|
||||||
|
|
||||||
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)
|
# --- ALTER MODUS (Vokabeln) ---
|
||||||
return jsonify(vocab_list)
|
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()
|
||||||
|
|
||||||
def expand_variants(text):
|
if not row:
|
||||||
segments = [s.strip() for s in text.split(';')]
|
return jsonify({'error': 'Keine Vokabeln gefunden.'})
|
||||||
possibilities = []
|
|
||||||
|
|
||||||
for seg in segments:
|
question_text = ""
|
||||||
if '/-' in seg:
|
answer_text = ""
|
||||||
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
|
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]
|
||||||
|
|
||||||
def clean_text_for_comparison(text):
|
return jsonify({
|
||||||
text = text.lower().strip()
|
'type': 'vocab',
|
||||||
if text.startswith('to '):
|
'question': question_text,
|
||||||
text = text[3:].strip()
|
'answer': answer_text,
|
||||||
return text
|
'page': row[2]
|
||||||
|
})
|
||||||
|
|
||||||
@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:
|
correct_simple = data.get('correct_simple', '')
|
||||||
return jsonify({'status': 'correct', 'msg': 'Richtig!'})
|
correct_participle = data.get('correct_participle', '')
|
||||||
|
|
||||||
best_ratio = 0
|
# Wir müssen "was/were" oder "burnt/burned" handhaben -> split am slash
|
||||||
best_match = ""
|
def check_part(user_in, correct_str):
|
||||||
for correct_option in valid_answers_clean:
|
variants = [x.strip().lower() for x in correct_str.split('/')]
|
||||||
ratio = SequenceMatcher(None, clean_user, correct_option).ratio()
|
return user_in in variants
|
||||||
if ratio > best_ratio:
|
|
||||||
best_ratio = ratio
|
|
||||||
best_match = correct_option
|
|
||||||
|
|
||||||
threshold = 0.75
|
is_simple_ok = check_part(user_simple, correct_simple)
|
||||||
if len(best_match) <= 4:
|
is_participle_ok = check_part(user_participle, correct_participle)
|
||||||
threshold = 0.8
|
|
||||||
|
|
||||||
if best_ratio >= threshold:
|
if is_simple_ok and is_participle_ok:
|
||||||
return jsonify({
|
return jsonify({'status': 'correct', 'msg': 'Perfekt! Beide Formen richtig.'})
|
||||||
'status': 'typo',
|
else:
|
||||||
'msg': f'Fast richtig! Meintest du: "{best_match}"? (Es war "{user_input}")'
|
return jsonify({
|
||||||
})
|
'status': 'wrong',
|
||||||
|
'msg': f'Leider falsch. Lösung: {correct_simple} | {correct_participle}'
|
||||||
|
})
|
||||||
|
|
||||||
return jsonify({
|
# --- ALTER CHECK (Vokabeln) ---
|
||||||
'status': 'wrong',
|
else:
|
||||||
'msg': f'Leider falsch. Versuche es erneut.'
|
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!'})
|
||||||
|
|
||||||
|
# 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}")'
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify({'status': 'wrong', 'msg': f'Falsch. Lösung: {db_answer}'})
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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)
|
||||||
Loading…
x
Reference in New Issue
Block a user