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 ---
|
# --- 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):
|
def is_middle_relevant(text):
|
||||||
if not text: return False
|
if not text: return False
|
||||||
signals = [' m', ' f', ' n', 'm.', 'f.', 'n.', 'pl', 'sg', 'gen', 'dat', 'akk', 'abl', 'perf', 'ppp']
|
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
|
return True
|
||||||
|
|
||||||
def clean_german_answers(text):
|
def clean_german_answers(text):
|
||||||
# Alles in Klammern entfernen und splitten
|
|
||||||
text = re.sub(r'\(.*?\)', '', text)
|
text = re.sub(r'\(.*?\)', '', text)
|
||||||
text = re.sub(r'\[.*?\]', '', text)
|
text = re.sub(r'\[.*?\]', '', text)
|
||||||
parts = re.split(r'[;,]', text)
|
parts = re.split(r'[;,]', text)
|
||||||
return [p.strip() for p in parts if p.strip()]
|
return [p.strip() for p in parts if p.strip()]
|
||||||
|
|
||||||
def check_middle_logic(user_in, correct):
|
def check_middle_logic(user_in, correct):
|
||||||
"""Prüft Grammatik (erlaubt unvollständige Eingaben)"""
|
# 1. Normalisieren (Striche wegdenken für Vergleich)
|
||||||
u = user_in.strip().lower()
|
u_norm = normalize_latin(user_in.strip().lower())
|
||||||
c = correct.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()]
|
def get_parts(s): return [x.strip() for x in s.split(',') if x.strip()]
|
||||||
|
|
||||||
u_parts = get_parts(u)
|
u_parts = get_parts(u_norm)
|
||||||
c_parts = get_parts(c)
|
c_parts = get_parts(c_norm)
|
||||||
|
|
||||||
if not u_parts: return 'wrong'
|
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)
|
all_user_parts_correct = all(p in c_parts for p in u_parts)
|
||||||
|
|
||||||
if all_user_parts_correct:
|
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'
|
return 'correct'
|
||||||
|
|
||||||
# Tippfehler-Check
|
ratio = SequenceMatcher(None, u_norm, c_norm).ratio()
|
||||||
ratio = SequenceMatcher(None, u, c).ratio()
|
|
||||||
if ratio > 0.85: return 'typo'
|
if ratio > 0.85: return 'typo'
|
||||||
|
|
||||||
return 'wrong'
|
return 'wrong'
|
||||||
|
|
||||||
def check_string_match(user_in, correct):
|
def check_string_match(user_in, correct):
|
||||||
"""Standard String Vergleich"""
|
u_norm = normalize_latin(user_in.strip().lower())
|
||||||
u = user_in.strip().lower()
|
c_norm = normalize_latin(correct.strip().lower())
|
||||||
c = correct.strip().lower()
|
|
||||||
if not u: return 'wrong'
|
|
||||||
|
|
||||||
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'
|
if ratio > 0.85: return 'typo'
|
||||||
return 'wrong'
|
return 'wrong'
|
||||||
|
|
||||||
@ -161,28 +169,22 @@ def check():
|
|||||||
inputs = data.get('inputs', [])
|
inputs = data.get('inputs', [])
|
||||||
attempt_num = data.get('attempt', 1)
|
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)
|
results = [None] * len(inputs)
|
||||||
|
|
||||||
correct_items = 0
|
correct_items = 0
|
||||||
total_items = len(inputs)
|
total_items = len(inputs)
|
||||||
has_incomplete = False
|
has_incomplete = False
|
||||||
|
|
||||||
# 1. GRAMMATIK (Mitte) direkt prüfen (feste Position)
|
# 1. GRAMMATIK
|
||||||
for i, inp in enumerate(inputs):
|
for i, inp in enumerate(inputs):
|
||||||
if inp.get('type') == 'middle':
|
if inp.get('type') == 'middle':
|
||||||
status = check_middle_logic(inp.get('value', ''), inp.get('correct', ''))
|
status = check_middle_logic(inp.get('value', ''), inp.get('correct', ''))
|
||||||
msg = 'Unvollständig! Da fehlt noch etwas.' if status == 'incomplete' else ''
|
msg = 'Unvollständig! Da fehlt noch etwas.' if status == 'incomplete' else ''
|
||||||
if status == 'incomplete': has_incomplete = True
|
if status == 'incomplete': has_incomplete = True
|
||||||
|
|
||||||
results[i] = {'status': status, 'correct': inp.get('correct', ''), 'msg': msg}
|
results[i] = {'status': status, 'correct': inp.get('correct', ''), 'msg': msg}
|
||||||
if status in ['correct', 'typo']: correct_items += 1
|
if status in ['correct', 'typo']: correct_items += 1
|
||||||
|
|
||||||
# 2. DEUTSCH (German) - Pool-Logik für Reihenfolge-Unabhängigkeit
|
# 2. DEUTSCH
|
||||||
|
german_user_inputs = []
|
||||||
# Sammle alle deutschen User-Eingaben und ihre Indizes
|
|
||||||
german_user_inputs = [] # Liste von {'index': 1, 'value': '...'}
|
|
||||||
# Sammle alle korrekten deutschen Lösungen (Pool)
|
|
||||||
german_correct_pool = []
|
german_correct_pool = []
|
||||||
|
|
||||||
for i, inp in enumerate(inputs):
|
for i, inp in enumerate(inputs):
|
||||||
@ -190,45 +192,33 @@ def check():
|
|||||||
german_user_inputs.append({'index': i, 'value': inp.get('value', '')})
|
german_user_inputs.append({'index': i, 'value': inp.get('value', '')})
|
||||||
german_correct_pool.append(inp.get('correct', ''))
|
german_correct_pool.append(inp.get('correct', ''))
|
||||||
|
|
||||||
# Welche Pool-Einträge wurden schon "verbraucht"?
|
|
||||||
used_pool_indices = set()
|
used_pool_indices = set()
|
||||||
|
|
||||||
# Jetzt prüfen wir jede User-Eingabe gegen den GANZEN Pool
|
|
||||||
for u_item in german_user_inputs:
|
for u_item in german_user_inputs:
|
||||||
idx = u_item['index']
|
idx = u_item['index']
|
||||||
val = u_item['value']
|
val = u_item['value']
|
||||||
found_match = False
|
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):
|
for pool_i, pool_val in enumerate(german_correct_pool):
|
||||||
if pool_i in used_pool_indices:
|
if pool_i in used_pool_indices: continue
|
||||||
continue
|
|
||||||
|
|
||||||
# Standard Prüfung für Deutsch
|
|
||||||
status = check_string_match(val, pool_val)
|
status = check_string_match(val, pool_val)
|
||||||
|
|
||||||
if status in ['correct', 'typo']:
|
if status in ['correct', 'typo']:
|
||||||
# Treffer!
|
|
||||||
results[idx] = {'status': status, 'correct': pool_val}
|
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
|
correct_items += 1
|
||||||
found_match = True
|
found_match = True
|
||||||
break
|
break
|
||||||
|
|
||||||
# Wenn KEIN Match im Pool gefunden wurde
|
|
||||||
if not found_match:
|
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', '')
|
original_correct = inputs[idx].get('correct', '')
|
||||||
results[idx] = {'status': 'wrong', 'correct': original_correct}
|
results[idx] = {'status': 'wrong', 'correct': original_correct}
|
||||||
|
|
||||||
# Punkteberechnung
|
|
||||||
score_fraction = 0
|
score_fraction = 0
|
||||||
if total_items > 0:
|
if total_items > 0:
|
||||||
base_score = correct_items / total_items
|
base_score = correct_items / total_items
|
||||||
if attempt_num > 1:
|
if attempt_num > 1: base_score = base_score * 0.5
|
||||||
base_score = base_score * 0.5
|
|
||||||
score_fraction = base_score
|
score_fraction = base_score
|
||||||
|
|
||||||
all_correct = (correct_items == total_items)
|
all_correct = (correct_items == total_items)
|
||||||
@ -241,5 +231,4 @@ def check():
|
|||||||
})
|
})
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Production settings
|
|
||||||
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