app/app.py aktualisiert
This commit is contained in:
parent
68ce167c7a
commit
2405da477d
77
app/app.py
77
app/app.py
@ -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)
|
||||
Loading…
x
Reference in New Issue
Block a user