From 862626bba1e17c2eb82bbd4205a9d246fd05e737 Mon Sep 17 00:00:00 2001 From: jamulix Date: Tue, 21 Oct 2025 20:22:24 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Implementiere=20echte=20Zeitmessung=20f?= =?UTF-8?q?=C3=BCr=20pr=C3=A4zise=20Tippstatistiken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aider (deepseek/deepseek-reasoner) --- app.py | 33 +++++++++++++++++--------- static/js/script.js | 58 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/app.py b/app.py index 011d53e..999f0e0 100644 --- a/app.py +++ b/app.py @@ -318,11 +318,12 @@ def recreate_lessons(): @app.route('/update_progress', methods=['POST']) def update_progress(): - """Aktualisiert den Tippfortschritt""" + """Aktualisiert den Tippfortschritt mit echter Zeitmessung""" try: data = request.get_json() user_input = data.get('user_input', '') current_text = data.get('current_text', '') + elapsed_time = data.get('elapsed_time', 0) # in Sekunden # Hole den aktuellen Fortschritt progress = Progress.query.first() @@ -335,8 +336,8 @@ def update_progress(): # Speichere in der Datenbank db.session.commit() - # Berechne Statistiken - stats = calculate_statistics(user_input, current_text) + # Berechne Statistiken mit echter Zeit + stats = calculate_statistics(user_input, current_text, elapsed_time) return jsonify(stats) else: @@ -346,24 +347,34 @@ def update_progress(): print(f"Fehler in update_progress: {e}") return jsonify({'error': 'Interner Serverfehler'}), 500 -def calculate_statistics(user_input, current_text): - """Berechnet Tippstatistiken""" +def calculate_statistics(user_input, current_text, elapsed_time): + """Berechnet Tippstatistiken mit echter Zeitmessung""" correct_chars = 0 incorrect_chars = 0 total_chars = len(user_input) + # Zähle korrekte und falsche Zeichen for i, char in enumerate(user_input): if i < len(current_text) and char == current_text[i]: correct_chars += 1 else: incorrect_chars += 1 - # Berechne verschiedene Metriken + # Berechne Metriken basierend auf echter Zeit + if total_chars > 0 and elapsed_time > 0: + # Zeichen pro Sekunde + chars_per_second = total_chars / elapsed_time + # Wörter pro Minute (Annahme: 1 Wort = 5 Zeichen) + words_per_minute = (total_chars / 5) / (elapsed_time / 60) + # Durchschnittliche Zeit pro Zeichen + duration_per_char = elapsed_time / total_chars + else: + chars_per_second = 0 + words_per_minute = 0 + duration_per_char = 0 + + # Fehlerrate und Genauigkeit error_rate = (incorrect_chars / total_chars * 100) if total_chars > 0 else 0 - time_taken = total_chars * 0.05 # Simulierte Zeit - typing_speed = (total_chars / time_taken * 60) if time_taken > 0 else 0 - duration_per_char = (time_taken / total_chars) if total_chars > 0 and time_taken > 0 else 0 - words_per_minute = ((total_chars / 5) / (time_taken / 60)) if total_chars > 0 and time_taken > 0 else 0 accuracy = (correct_chars / total_chars * 100) if total_chars > 0 else 0 return { @@ -371,7 +382,7 @@ def calculate_statistics(user_input, current_text): 'incorrect_chars': incorrect_chars, 'total_chars': total_chars, 'error_rate': round(error_rate, 1), - 'typing_speed': round(typing_speed, 2), + 'typing_speed': round(chars_per_second, 2), 'duration_per_char': round(duration_per_char, 3), 'words_per_minute': round(words_per_minute, 2), 'accuracy': round(accuracy, 2) diff --git a/static/js/script.js b/static/js/script.js index e6c8b4d..63ad202 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -15,10 +15,22 @@ document.addEventListener('DOMContentLoaded', function() { // Aktuelle Textdaten let currentText = window.initialText; + let startTime = null; + let timerInterval = null; // Fokus auf das Eingabefeld setzen userInput.focus(); + // Starte die Zeitmessung beim ersten Tastendruck + userInput.addEventListener('keydown', function(event) { + if (!startTime && event.key.length === 1) { // Nur Buchstaben, Zahlen, etc. + startTime = new Date(); + // Starte Timer für Echtzeit-Update + if (timerInterval) clearInterval(timerInterval); + timerInterval = setInterval(updateTimer, 100); + } + }); + // Aktualisiere die Statistik bei Eingabe userInput.addEventListener('input', function() { const userValue = this.value; @@ -26,6 +38,10 @@ document.addEventListener('DOMContentLoaded', function() { // Wenn der Text vollständig getippt wurde, automatisch zur nächsten Zeile if (userValue === currentText) { + if (timerInterval) { + clearInterval(timerInterval); + timerInterval = null; + } setTimeout(function() { fetch('/next_text', { method: 'POST', @@ -42,16 +58,33 @@ document.addEventListener('DOMContentLoaded', function() { userInput.value = ''; userInput.focus(); // Fokus zurück auf Eingabefeld updateStatistics('', currentText); + // Zurücksetzen der Zeitmessung + startTime = null; }) .catch(error => console.error('Fehler:', error)); }, 100); // Kurze Verzögerung für bessere Benutzererfahrung } }); + // Timer für Echtzeit-Update der Statistik + function updateTimer() { + if (startTime) { + const userValue = userInput.value; + updateStatistics(userValue, currentText); + } + } + // Event-Listener für Lektions-Buttons document.querySelectorAll('.lesson-btn').forEach(button => { button.addEventListener('click', function() { const lessonIndex = parseInt(this.getAttribute('data-lesson-index')); + // Stoppe Timer und setze Zeit zurück + if (timerInterval) { + clearInterval(timerInterval); + timerInterval = null; + } + startTime = null; + fetch('/set_lesson', { method: 'POST', headers: { @@ -78,8 +111,15 @@ document.addEventListener('DOMContentLoaded', function() { }); }); - // Nächste Zeile Button + // Nächster Text Button nextBtn.addEventListener('click', function() { + // Stoppe Timer und setze Zeit zurück + if (timerInterval) { + clearInterval(timerInterval); + timerInterval = null; + } + startTime = null; + fetch('/next_text', { method: 'POST', headers: { @@ -117,6 +157,13 @@ document.addEventListener('DOMContentLoaded', function() { // Statistik aktualisieren function updateStatistics(userInput, currentText) { + // Berechne verstrichene Zeit + let elapsedTime = 0; + if (startTime) { + const now = new Date(); + elapsedTime = (now - startTime) / 1000; // in Sekunden + } + // Sende Daten an den Server fetch('/update_progress', { method: 'POST', @@ -125,19 +172,22 @@ document.addEventListener('DOMContentLoaded', function() { }, body: JSON.stringify({ user_input: userInput, - current_text: currentText + current_text: currentText, + elapsed_time: elapsedTime }) }) .then(response => response.json()) .then(data => { // Aktualisiere die Anzeige speedElement.textContent = data.typing_speed; - durationElement.textContent = data.duration_per_char + 's'; + durationElement.textContent = data.duration_per_char.toFixed(3) + 's'; errorRateElement.textContent = data.error_rate + '%'; correctElement.textContent = data.correct_chars; incorrectElement.textContent = data.incorrect_chars; totalElement.textContent = data.total_chars; - wpmElement.textContent = data.words_per_minute; + wpmElement.textContent = data.words_per_minute.toFixed(2); + // Aktualisiere die Genauigkeit + document.getElementById('accuracy').textContent = data.accuracy.toFixed(1) + '%'; // Aktualisiere die Textfarben updateTextHighlighting(userInput, currentText);