feat: Implementiere echte Zeitmessung für präzise Tippstatistiken
Co-authored-by: aider (deepseek/deepseek-reasoner) <aider@aider.chat>
This commit is contained in:
parent
480575a2b2
commit
862626bba1
2 changed files with 76 additions and 15 deletions
33
app.py
33
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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue