app/app.py hinzugefügt
This commit is contained in:
parent
e7a6a473d0
commit
10c9e0529f
206
app/app.py
Normal file
206
app/app.py
Normal file
@ -0,0 +1,206 @@
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
import re
|
||||
import psycopg2
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
from difflib import SequenceMatcher
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def get_db_connection():
|
||||
max_retries = 10
|
||||
for i in range(max_retries):
|
||||
try:
|
||||
conn = psycopg2.connect(
|
||||
host=os.environ.get('DB_HOST', 'postgres-lat'),
|
||||
database=os.environ.get('DB_NAME', 'latein'),
|
||||
user=os.environ.get('DB_USER', 'latein'),
|
||||
password=os.environ.get('DB_PASS', 'latein')
|
||||
)
|
||||
return conn
|
||||
except psycopg2.OperationalError:
|
||||
time.sleep(2)
|
||||
continue
|
||||
raise Exception("DB Connection failed")
|
||||
|
||||
def init_db():
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor()
|
||||
if os.path.exists('init.sql'):
|
||||
with open('init.sql', 'r', encoding='utf-8') as f:
|
||||
cur.execute(f.read())
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
# Start-Initialisierung
|
||||
init_db()
|
||||
|
||||
# --- HELPER FUNKTIONEN ---
|
||||
|
||||
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']
|
||||
t = text.lower()
|
||||
ignore = ['adv', 'präp', 'konj', 'subj', 'interj']
|
||||
is_signal = any(s in t for s in signals)
|
||||
is_ignore = any(i in t for i in ignore)
|
||||
if is_ignore and not is_signal: return False
|
||||
return True
|
||||
|
||||
def clean_german_answers(text):
|
||||
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 auf Unvollständigkeit (Komma-Trennung)"""
|
||||
u = user_in.strip().lower()
|
||||
c = correct.strip().lower()
|
||||
|
||||
if u == c: 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)
|
||||
|
||||
if not u_parts: return 'wrong'
|
||||
|
||||
all_user_parts_correct = all(p in c_parts for p in u_parts)
|
||||
|
||||
if all_user_parts_correct:
|
||||
if len(u_parts) < len(c_parts):
|
||||
return 'incomplete'
|
||||
return 'correct'
|
||||
|
||||
ratio = SequenceMatcher(None, u, c).ratio()
|
||||
if ratio > 0.85: return 'typo'
|
||||
|
||||
return 'wrong'
|
||||
|
||||
def check_string_match(user_in, correct):
|
||||
u = user_in.strip().lower()
|
||||
c = correct.strip().lower()
|
||||
if u == c: return 'correct'
|
||||
ratio = SequenceMatcher(None, u, c).ratio()
|
||||
if ratio > 0.85: return 'typo'
|
||||
return 'wrong'
|
||||
|
||||
# --- ROUTEN ---
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/api/lessons')
|
||||
def get_lessons():
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT DISTINCT lesson FROM vocabulary")
|
||||
rows = cur.fetchall()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
lessons = [r[0] for r in rows]
|
||||
|
||||
# Sortierung: Erst Zahlen, dann Strings, "Original" ganz hinten
|
||||
def sort_key(val):
|
||||
if val.isdigit(): return (0, int(val))
|
||||
if val.startswith("Original"): return (2, val)
|
||||
return (1, val)
|
||||
|
||||
lessons.sort(key=sort_key)
|
||||
return jsonify(lessons)
|
||||
|
||||
@app.route('/api/question', methods=['POST'])
|
||||
def get_question():
|
||||
data = request.json
|
||||
selected_lessons = data.get('lessons', [])
|
||||
ask_middle_setting = data.get('askMiddle', True)
|
||||
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor()
|
||||
query = "SELECT id, lesson, latin, middle, german FROM vocabulary WHERE lesson = ANY(%s) ORDER BY RANDOM() LIMIT 1"
|
||||
cur.execute(query, (selected_lessons,))
|
||||
row = cur.fetchone()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
if not row: return jsonify({'error': 'Keine Vokabeln gefunden'})
|
||||
|
||||
vid, lesson, latin, middle, german_raw = row
|
||||
german_answers = clean_german_answers(german_raw)
|
||||
|
||||
middle_req = False
|
||||
hint = ""
|
||||
if middle:
|
||||
if ask_middle_setting and is_middle_relevant(middle):
|
||||
middle_req = True
|
||||
else:
|
||||
hint = middle
|
||||
|
||||
return jsonify({
|
||||
'id': vid,
|
||||
'latin': latin,
|
||||
'hint': hint,
|
||||
'middle_req': middle_req,
|
||||
'middle_correct': middle if middle_req else "",
|
||||
'german_count': len(german_answers),
|
||||
'german_correct': german_answers
|
||||
})
|
||||
|
||||
@app.route('/api/check', methods=['POST'])
|
||||
def check():
|
||||
data = request.json
|
||||
inputs = data.get('inputs', [])
|
||||
attempt_num = data.get('attempt', 1)
|
||||
|
||||
results = []
|
||||
total_items = len(inputs)
|
||||
correct_items = 0
|
||||
has_incomplete = False
|
||||
|
||||
for inp in inputs:
|
||||
user_val = inp.get('value', '')
|
||||
correct_val = inp.get('correct', '')
|
||||
field_type = inp.get('type', 'german')
|
||||
|
||||
status = 'wrong'
|
||||
|
||||
if field_type == 'middle':
|
||||
status = check_middle_logic(user_val, correct_val)
|
||||
else:
|
||||
status = check_string_match(user_val, correct_val)
|
||||
|
||||
res_entry = {'status': status, 'correct': correct_val}
|
||||
|
||||
if status == 'incomplete':
|
||||
res_entry['msg'] = 'Unvollständig! Da fehlt noch etwas.'
|
||||
has_incomplete = True
|
||||
|
||||
results.append(res_entry)
|
||||
|
||||
if status in ['correct', 'typo']:
|
||||
correct_items += 1
|
||||
|
||||
score_fraction = 0
|
||||
if total_items > 0:
|
||||
base_score = correct_items / total_items
|
||||
if attempt_num > 1:
|
||||
base_score = base_score * 0.5
|
||||
score_fraction = base_score
|
||||
|
||||
all_correct = (correct_items == total_items)
|
||||
|
||||
return jsonify({
|
||||
'results': results,
|
||||
'all_correct': all_correct,
|
||||
'score': score_fraction,
|
||||
'has_incomplete': has_incomplete
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
Loading…
x
Reference in New Issue
Block a user