app/app.py aktualisiert

This commit is contained in:
sascha 2026-01-28 22:15:56 +00:00
parent 68ce167c7a
commit 2405da477d

View File

@ -38,6 +38,19 @@ init_db()
# --- HELPER ---
def normalize_latin(text):
"""Entfernt Makrons für den toleranten Vergleich (ā -> a)"""
if not text: return ""
replacements = {
'ā': 'a', 'ē': 'e', 'ī': 'i', 'ō': 'o', 'ū': 'u',
'Ā': 'A', 'Ē': 'E', 'Ī': 'I', 'Ō': 'O', 'Ū': 'U',
'ȳ': 'y', 'Ȳ': 'Y'
}
t = text
for k, v in replacements.items():
t = t.replace(k, v)
return t
def is_middle_relevant(text):
if not text: return False
signals = [' m', ' f', ' n', 'm.', 'f.', 'n.', 'pl', 'sg', 'gen', 'dat', 'akk', 'abl', 'perf', 'ppp']
@ -49,50 +62,45 @@ def is_middle_relevant(text):
return True
def clean_german_answers(text):
# Alles in Klammern entfernen und splitten
text = re.sub(r'\(.*?\)', '', text)
text = re.sub(r'\[.*?\]', '', text)
parts = re.split(r'[;,]', text)
return [p.strip() for p in parts if p.strip()]
def check_middle_logic(user_in, correct):
"""Prüft Grammatik (erlaubt unvollständige Eingaben)"""
u = user_in.strip().lower()
c = correct.strip().lower()
# 1. Normalisieren (Striche wegdenken für Vergleich)
u_norm = normalize_latin(user_in.strip().lower())
c_norm = normalize_latin(correct.strip().lower())
if u == c: return 'correct'
# 2. Exakter Abgleich (auf Basis der normalisierten Version)
if u_norm == c_norm: return 'correct'
def get_parts(s): return [x.strip() for x in s.split(',') if x.strip()]
u_parts = get_parts(u)
c_parts = get_parts(c)
u_parts = get_parts(u_norm)
c_parts = get_parts(c_norm)
if not u_parts: return 'wrong'
# Sind alle eingegebenen Teile in der Lösung vorhanden?
all_user_parts_correct = all(p in c_parts for p in u_parts)
if all_user_parts_correct:
# Wenn weniger Teile als nötig -> unvollständig
if len(u_parts) < len(c_parts):
return 'incomplete'
if len(u_parts) < len(c_parts): return 'incomplete'
return 'correct'
# Tippfehler-Check
ratio = SequenceMatcher(None, u, c).ratio()
ratio = SequenceMatcher(None, u_norm, c_norm).ratio()
if ratio > 0.85: return 'typo'
return 'wrong'
def check_string_match(user_in, correct):
"""Standard String Vergleich"""
u = user_in.strip().lower()
c = correct.strip().lower()
if not u: return 'wrong'
u_norm = normalize_latin(user_in.strip().lower())
c_norm = normalize_latin(correct.strip().lower())
if u == c: return 'correct'
if not u_norm: return 'wrong'
if u_norm == c_norm: return 'correct'
ratio = SequenceMatcher(None, u, c).ratio()
ratio = SequenceMatcher(None, u_norm, c_norm).ratio()
if ratio > 0.85: return 'typo'
return 'wrong'
@ -161,28 +169,22 @@ def check():
inputs = data.get('inputs', [])
attempt_num = data.get('attempt', 1)
# Wir initialisieren die Ergebnis-Liste mit "falsch", damit wir sie später füllen können
results = [None] * len(inputs)
correct_items = 0
total_items = len(inputs)
has_incomplete = False
# 1. GRAMMATIK (Mitte) direkt prüfen (feste Position)
# 1. GRAMMATIK
for i, inp in enumerate(inputs):
if inp.get('type') == 'middle':
status = check_middle_logic(inp.get('value', ''), inp.get('correct', ''))
msg = 'Unvollständig! Da fehlt noch etwas.' if status == 'incomplete' else ''
if status == 'incomplete': has_incomplete = True
results[i] = {'status': status, 'correct': inp.get('correct', ''), 'msg': msg}
if status in ['correct', 'typo']: correct_items += 1
# 2. DEUTSCH (German) - Pool-Logik für Reihenfolge-Unabhängigkeit
# Sammle alle deutschen User-Eingaben und ihre Indizes
german_user_inputs = [] # Liste von {'index': 1, 'value': '...'}
# Sammle alle korrekten deutschen Lösungen (Pool)
# 2. DEUTSCH
german_user_inputs = []
german_correct_pool = []
for i, inp in enumerate(inputs):
@ -190,45 +192,33 @@ def check():
german_user_inputs.append({'index': i, 'value': inp.get('value', '')})
german_correct_pool.append(inp.get('correct', ''))
# Welche Pool-Einträge wurden schon "verbraucht"?
used_pool_indices = set()
# Jetzt prüfen wir jede User-Eingabe gegen den GANZEN Pool
for u_item in german_user_inputs:
idx = u_item['index']
val = u_item['value']
found_match = False
matched_correct_val = ""
# Suche im Pool nach einem Match, der noch nicht benutzt wurde
for pool_i, pool_val in enumerate(german_correct_pool):
if pool_i in used_pool_indices:
continue
if pool_i in used_pool_indices: continue
# Standard Prüfung für Deutsch
status = check_string_match(val, pool_val)
if status in ['correct', 'typo']:
# Treffer!
results[idx] = {'status': status, 'correct': pool_val}
used_pool_indices.add(pool_i) # Lösung als "benutzt" markieren
used_pool_indices.add(pool_i)
correct_items += 1
found_match = True
break
# Wenn KEIN Match im Pool gefunden wurde
if not found_match:
# Wir zeigen als Lösung einfach die an, die ursprünglich an dieser Position stand
# (auch wenn die Position eigentlich egal ist, irgendwas müssen wir anzeigen)
original_correct = inputs[idx].get('correct', '')
results[idx] = {'status': 'wrong', 'correct': original_correct}
# Punkteberechnung
score_fraction = 0
if total_items > 0:
base_score = correct_items / total_items
if attempt_num > 1:
base_score = base_score * 0.5
if attempt_num > 1: base_score = base_score * 0.5
score_fraction = base_score
all_correct = (correct_items == total_items)
@ -241,5 +231,4 @@ def check():
})
if __name__ == '__main__':
# Production settings
app.run(host='0.0.0.0', port=5000)