-m "Auf ZPM umgestellt"
This commit is contained in:
parent
884867ad09
commit
80c40ab983
8 changed files with 494 additions and 15 deletions
87
app.py
87
app.py
|
|
@ -140,6 +140,36 @@ with app.app_context():
|
|||
db.create_all()
|
||||
print("Datenbanktabellen wurden initialisiert")
|
||||
|
||||
# Prüfe ob die lesson_statistic Tabelle die alte Spalte chars_per_second hat
|
||||
from sqlalchemy import inspect, text
|
||||
inspector = inspect(db.engine)
|
||||
if 'lesson_statistic' in inspector.get_table_names():
|
||||
columns = [col['name'] for col in inspector.get_columns('lesson_statistic')]
|
||||
if 'chars_per_second' in columns and 'chars_per_minute' not in columns:
|
||||
print("Aktualisiere Datenbanktabelle lesson_statistic...")
|
||||
# Temporäre Tabelle erstellen
|
||||
db.session.execute(text('''
|
||||
CREATE TABLE lesson_statistic_new (
|
||||
id INTEGER PRIMARY KEY,
|
||||
lesson_index INTEGER NOT NULL,
|
||||
chars_per_minute FLOAT DEFAULT 0.0,
|
||||
error_rate FLOAT DEFAULT 0.0,
|
||||
wpm FLOAT DEFAULT 0.0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
'''))
|
||||
# Daten umrechnen und übertragen (chars_per_second * 60)
|
||||
db.session.execute(text('''
|
||||
INSERT INTO lesson_statistic_new (id, lesson_index, chars_per_minute, error_rate, wpm, created_at)
|
||||
SELECT id, lesson_index, chars_per_second * 60, error_rate, wpm, created_at
|
||||
FROM lesson_statistic
|
||||
'''))
|
||||
# Alte Tabelle löschen und neue umbenennen
|
||||
db.session.execute(text('DROP TABLE lesson_statistic'))
|
||||
db.session.execute(text('ALTER TABLE lesson_statistic_new RENAME TO lesson_statistic'))
|
||||
db.session.commit()
|
||||
print("Datenbanktabelle lesson_statistic erfolgreich aktualisiert")
|
||||
|
||||
# Versuche Lektionen zu laden
|
||||
load_lessons()
|
||||
|
||||
|
|
@ -363,14 +393,14 @@ def calculate_statistics(user_input, current_text, elapsed_time):
|
|||
|
||||
# Berechne Metriken basierend auf echter Zeit
|
||||
if total_chars > 0 and elapsed_time > 0:
|
||||
# Zeichen pro Sekunde (auf eine Nachkommastelle gerundet)
|
||||
chars_per_second = total_chars / elapsed_time
|
||||
# Zeichen pro Minute (ganzzahlig)
|
||||
chars_per_minute = (total_chars / elapsed_time) * 60
|
||||
# Wörter pro Minute gemäß Formel: (Gesamtzahl der korrekt getippten Zeichen/5)/((Zeit in Sekunden)/60)
|
||||
words_per_minute = (correct_chars / 5) / (elapsed_time / 60)
|
||||
# Durchschnittliche Zeit pro Zeichen
|
||||
duration_per_char = elapsed_time / total_chars
|
||||
else:
|
||||
chars_per_second = 0
|
||||
chars_per_minute = 0
|
||||
words_per_minute = 0
|
||||
duration_per_char = 0
|
||||
|
||||
|
|
@ -383,7 +413,7 @@ def calculate_statistics(user_input, current_text, elapsed_time):
|
|||
'incorrect_chars': incorrect_chars,
|
||||
'total_chars': total_chars,
|
||||
'error_rate': round(error_rate, 1),
|
||||
'typing_speed': round(chars_per_second, 1), # Auf eine Nachkommastelle
|
||||
'typing_speed': round(chars_per_minute), # Ganzzahlig
|
||||
'duration_per_char': round(duration_per_char, 3),
|
||||
'words_per_minute': round(words_per_minute, 2), # Auf zwei Nachkommastellen
|
||||
'accuracy': round(accuracy, 2)
|
||||
|
|
@ -470,7 +500,7 @@ def save_lesson_statistics():
|
|||
try:
|
||||
data = request.get_json()
|
||||
lesson_index = data.get('lesson_index')
|
||||
chars_per_second = data.get('chars_per_second')
|
||||
chars_per_minute = data.get('chars_per_minute')
|
||||
error_rate = data.get('error_rate')
|
||||
wpm = data.get('wpm')
|
||||
|
||||
|
|
@ -480,7 +510,7 @@ def save_lesson_statistics():
|
|||
# Neue Statistik für die Lektion erstellen
|
||||
lesson_stat = LessonStatistic(
|
||||
lesson_index=lesson_index,
|
||||
chars_per_second=chars_per_second,
|
||||
chars_per_minute=chars_per_minute,
|
||||
error_rate=error_rate,
|
||||
wpm=wpm
|
||||
)
|
||||
|
|
@ -494,6 +524,51 @@ def save_lesson_statistics():
|
|||
print(f"Fehler in save_lesson_statistics: {e}")
|
||||
return jsonify({'error': 'Interner Serverfehler'}), 500
|
||||
|
||||
@app.route('/statistics')
|
||||
def statistics():
|
||||
"""Zeigt die Statistik-Seite mit Diagrammen"""
|
||||
try:
|
||||
# Hole den aktuellen Fortschritt für die voreingestellte Lektion
|
||||
progress = Progress.query.first()
|
||||
current_lesson_index = progress.current_text_index if progress else 0
|
||||
|
||||
# Prüfe für jede Lektion, ob Statistik-Daten vorhanden sind
|
||||
lesson_has_data = []
|
||||
for i in range(len(lessons)):
|
||||
has_data = LessonStatistic.query.filter_by(lesson_index=i).first() is not None
|
||||
lesson_has_data.append(has_data)
|
||||
|
||||
return render_template('statistics.html',
|
||||
lessons=lessons,
|
||||
current_lesson_index=current_lesson_index,
|
||||
lesson_has_data=lesson_has_data)
|
||||
except Exception as e:
|
||||
print(f"Fehler in statistics(): {e}")
|
||||
return render_template('statistics.html',
|
||||
lessons=[],
|
||||
current_lesson_index=0,
|
||||
lesson_has_data=[])
|
||||
|
||||
@app.route('/get_lesson_statistics/<int:lesson_index>')
|
||||
def get_lesson_statistics(lesson_index):
|
||||
"""Gibt die Statistiken für eine bestimmte Lektion zurück"""
|
||||
try:
|
||||
stats = LessonStatistic.query.filter_by(lesson_index=lesson_index)\
|
||||
.order_by(LessonStatistic.created_at.asc())\
|
||||
.all()
|
||||
|
||||
data = {
|
||||
'dates': [stat.created_at.strftime('%y-%m-%d') for stat in stats],
|
||||
'chars_per_minute': [stat.chars_per_minute for stat in stats],
|
||||
'error_rate': [stat.error_rate for stat in stats],
|
||||
'wpm': [stat.wpm for stat in stats]
|
||||
}
|
||||
|
||||
return jsonify(data)
|
||||
except Exception as e:
|
||||
print(f"Fehler in get_lesson_statistics: {e}")
|
||||
return jsonify({'error': 'Interner Serverfehler'}), 500
|
||||
|
||||
@app.route('/end_session', methods=['POST'])
|
||||
def end_session():
|
||||
"""Beendet eine Tipp-Session und speichert Statistiken"""
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
{
|
||||
"lesson": 9,
|
||||
"title": "Kurze Textpassagen",
|
||||
"text": "Das schnelle Tippen mit zehn Fingern spart viel Zeit. Mit Geduld und Übung wird jeder Tippfehler seltener. Eine gute Haltung und ruhige Atmung helfen beim Lernen.\nBleibe konzentriert, atme ruhig und halte den Blick auf den Text."
|
||||
"text": "Das schnelle Tippen mit zehn Fingern spart viel Zeit. Mit Geduld und Übung wird jeder Tippfehler seltener. Eine gute Haltung und ruhige Atmung helfen beim Lernen.\nBleibe konzentriert, atme ruhig und halte den Blick auf dem Text."
|
||||
},
|
||||
{
|
||||
"lesson": 10,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class Statistic(db.Model):
|
|||
class LessonStatistic(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
lesson_index = db.Column(db.Integer, nullable=False)
|
||||
chars_per_second = db.Column(db.Float, default=0.0)
|
||||
chars_per_minute = db.Column(db.Float, default=0.0)
|
||||
error_rate = db.Column(db.Float, default=0.0)
|
||||
wpm = db.Column(db.Float, default=0.0)
|
||||
created_at = db.Column(db.DateTime, default=db.func.now())
|
||||
|
|
|
|||
|
|
@ -101,6 +101,19 @@ body {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
/* Statistik Link Styling */
|
||||
.statistics-link {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.statistics-link:hover {
|
||||
color: #ffc107 !important;
|
||||
text-shadow: 0 0 10px #ffc107, 0 0 20px #ffc107;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
|
|
|
|||
|
|
@ -62,9 +62,27 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
|
||||
// Starte die Zeitmessung beim ersten Tastendruck und zähle Tastaturanschläge
|
||||
userInput.addEventListener('keydown', function(event) {
|
||||
const cursorPosition = userInput.selectionStart;
|
||||
const textLength = userInput.value.length;
|
||||
let shouldCountKey = false;
|
||||
|
||||
// Zähle Tastaturanschläge für alle Tasten, die keine Steuertasten sind (inkl. Backspace)
|
||||
// Beachte: event.key für Backspace ist 'Backspace'
|
||||
if (event.key.length === 1 || event.key === 'Backspace') {
|
||||
shouldCountKey = true;
|
||||
}
|
||||
// Pfeiltasten: Nur zählen, wenn sich der Cursor tatsächlich bewegt
|
||||
else if (event.key === 'ArrowLeft') {
|
||||
if (cursorPosition > 0) {
|
||||
shouldCountKey = true;
|
||||
}
|
||||
} else if (event.key === 'ArrowRight') {
|
||||
if (cursorPosition < textLength) {
|
||||
shouldCountKey = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCountKey) {
|
||||
if (!startTime) {
|
||||
startTime = new Date();
|
||||
}
|
||||
|
|
@ -95,9 +113,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
|
||||
// Speichere die Statistiken für diese Lektion
|
||||
const lessonIndex = window.currentLessonIndex;
|
||||
const charsPerSecond = parseFloat(speedElement.textContent);
|
||||
const charsPerMinute = parseInt(speedElement.textContent);
|
||||
const errorRate = parseFloat(errorRateElement.textContent);
|
||||
const wpm = parseFloat(wpmElement.textContent);
|
||||
const wpm = parseInt(wpmElement.textContent);
|
||||
|
||||
fetch('/save_lesson_statistics', {
|
||||
method: 'POST',
|
||||
|
|
@ -106,7 +124,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
},
|
||||
body: JSON.stringify({
|
||||
lesson_index: lessonIndex,
|
||||
chars_per_second: charsPerSecond,
|
||||
chars_per_minute: charsPerMinute,
|
||||
error_rate: errorRate,
|
||||
wpm: wpm
|
||||
})
|
||||
|
|
@ -272,12 +290,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
}
|
||||
|
||||
let incorrectKeyStrokes = totalKeyStrokes - correctKeyStrokes;
|
||||
let charsPerSecond = elapsedTime > 0 ? correctKeyStrokes / elapsedTime : 0;
|
||||
let charsPerMinute = elapsedTime > 0 ? (correctKeyStrokes / elapsedTime) * 60 : 0;
|
||||
let errorRate = correctKeyStrokes > 0 ? (incorrectKeyStrokes * 100.0) / correctKeyStrokes : 0;
|
||||
let wpm = elapsedTime > 0 ? (correctKeyStrokes / 5) / (elapsedTime / 60) : 0;
|
||||
|
||||
// Aktualisiere die Anzeige
|
||||
speedElement.textContent = charsPerSecond.toFixed(1);
|
||||
speedElement.textContent = Math.round(charsPerMinute);
|
||||
errorRateElement.textContent = errorRate.toFixed(1);
|
||||
correctElement.textContent = correctKeyStrokes;
|
||||
incorrectElement.textContent = incorrectKeyStrokes;
|
||||
|
|
@ -346,6 +364,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
updateTextHighlighting('', lines[currentLineIndex]);
|
||||
}
|
||||
|
||||
// Event-Listener für Statistik-Link
|
||||
document.querySelector('.statistics-link').addEventListener('click', function() {
|
||||
window.location.href = '/statistics';
|
||||
});
|
||||
|
||||
// Initialisiere Statistik und Textfarben
|
||||
updateLineDisplay();
|
||||
updateStatistics('', lines[currentLineIndex]);
|
||||
|
|
|
|||
|
|
@ -90,10 +90,10 @@
|
|||
<div class="col-md-6">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Statistik</h6>
|
||||
<h6 class="card-title statistics-link">Statistik</h6>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Zeichen pro Sekunde
|
||||
Zeichen pro Minute (ZPM)
|
||||
<span id="speed" class="badge bg-primary rounded-pill">0</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
|
|
|
|||
198
templates/statistics.html
Normal file
198
templates/statistics.html
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Statistik - Typewriter Trainer</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
</head>
|
||||
<body class="bg-light" data-bs-theme="light">
|
||||
<div class="container py-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-10">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h2 class="mb-0">Lektionsstatistik</h2>
|
||||
<button onclick="window.location.href='/'" class="btn btn-outline-light">
|
||||
Zurück zum Training
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Lektionen Auswahl -->
|
||||
<div class="mb-4">
|
||||
<h5 class="card-title">Lektion auswählen</h5>
|
||||
<div class="d-flex flex-wrap justify-content-start" id="lessons-container">
|
||||
{% for lesson in lessons %}
|
||||
<div class="mb-1 me-1" style="width: 40px;">
|
||||
{% if lesson_has_data[loop.index0] %}
|
||||
<div class="lesson-link {% if loop.index0 == current_lesson_index %}fw-bold text-danger{% else %}text-primary{% endif %}"
|
||||
style="cursor: pointer; font-size: 0.9rem; text-align: center; white-space: nowrap; min-height: 24px; display: flex; align-items: center; justify-content: center;"
|
||||
onclick="loadLessonStatistics({{ loop.index0 }})"
|
||||
data-lesson-index="{{ loop.index0 }}">
|
||||
{{ lesson.lesson if lesson.lesson is defined else loop.index0 + 1 }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="lesson-link text-muted"
|
||||
style="font-size: 0.9rem; text-align: center; white-space: nowrap; min-height: 24px; display: flex; align-items: center; justify-content: center;"
|
||||
data-lesson-index="{{ loop.index0 }}">
|
||||
{{ lesson.lesson if lesson.lesson is defined else loop.index0 + 1 }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Diagramm -->
|
||||
<div class="mb-4">
|
||||
<canvas id="statisticsChart" width="400" height="200"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script>
|
||||
let currentChart = null;
|
||||
|
||||
function loadLessonStatistics(lessonIndex) {
|
||||
// Aktualisiere die Lektionsmarkierung
|
||||
document.querySelectorAll('.lesson-link').forEach(link => {
|
||||
const index = parseInt(link.getAttribute('data-lesson-index'));
|
||||
if (index === lessonIndex) {
|
||||
link.classList.add('fw-bold', 'text-danger');
|
||||
link.classList.remove('text-primary');
|
||||
} else {
|
||||
link.classList.remove('fw-bold', 'text-danger');
|
||||
link.classList.add('text-primary');
|
||||
}
|
||||
});
|
||||
|
||||
// Lade die Statistiken für die ausgewählte Lektion
|
||||
fetch(`/get_lesson_statistics/${lessonIndex}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
console.error('Fehler:', data.error);
|
||||
return;
|
||||
}
|
||||
updateChart(data);
|
||||
})
|
||||
.catch(error => console.error('Fehler:', error));
|
||||
}
|
||||
|
||||
function updateChart(data) {
|
||||
const ctx = document.getElementById('statisticsChart').getContext('2d');
|
||||
|
||||
// Zerstöre vorheriges Diagramm falls vorhanden
|
||||
if (currentChart) {
|
||||
currentChart.destroy();
|
||||
}
|
||||
|
||||
currentChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.dates,
|
||||
datasets: [
|
||||
{
|
||||
label: 'ZPM (Zeichen pro Minute)',
|
||||
data: data.chars_per_minute,
|
||||
borderColor: 'blue',
|
||||
backgroundColor: 'rgba(0, 0, 255, 0.1)',
|
||||
tension: 0.1,
|
||||
yAxisID: 'y'
|
||||
},
|
||||
{
|
||||
label: 'Err (Fehler in %)',
|
||||
data: data.error_rate,
|
||||
borderColor: 'red',
|
||||
backgroundColor: 'rgba(255, 0, 0, 0.1)',
|
||||
tension: 0.1,
|
||||
yAxisID: 'y1'
|
||||
},
|
||||
{
|
||||
label: 'WPM (Wörter pro Minute)',
|
||||
data: data.wpm,
|
||||
borderColor: 'green',
|
||||
backgroundColor: 'rgba(0, 255, 0, 0.1)',
|
||||
tension: 0.1,
|
||||
yAxisID: 'y2'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Datum (YY-MM-DD)'
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 90,
|
||||
minRotation: 90
|
||||
}
|
||||
},
|
||||
y: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'left',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'ZPM'
|
||||
},
|
||||
min: 0,
|
||||
suggestedMax: Math.max(...data.chars_per_minute) * 1.1 || 1
|
||||
},
|
||||
y1: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Err (%)'
|
||||
},
|
||||
min: 0,
|
||||
suggestedMax: Math.max(...data.error_rate) * 1.1 || 1,
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
},
|
||||
},
|
||||
y2: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'WPM'
|
||||
},
|
||||
min: 0,
|
||||
suggestedMax: Math.max(...data.wpm) * 1.1 || 1,
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Lade initial die Statistiken für die aktuelle Lektion
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadLessonStatistics({{ current_lesson_index }});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
170
zpm_test.py
Normal file
170
zpm_test.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import sqlite3
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
|
||||
def test_zpm_measurement_and_storage():
|
||||
"""Testet die Messung und Speicherung von Zeichen pro Minute"""
|
||||
|
||||
# Datenbankverbindung herstellen - erstelle Datenbank falls nicht vorhanden
|
||||
db_path = 'typewriter.db'
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Erstelle die Tabelle falls sie nicht existiert
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS lesson_statistic (
|
||||
id INTEGER PRIMARY KEY,
|
||||
lesson_index INTEGER NOT NULL,
|
||||
chars_per_minute FLOAT DEFAULT 0.0,
|
||||
error_rate FLOAT DEFAULT 0.0,
|
||||
wpm FLOAT DEFAULT 0.0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
|
||||
try:
|
||||
# Prüfe, ob die Tabelle lesson_statistic existiert
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='lesson_statistic'")
|
||||
if not cursor.fetchone():
|
||||
print("Fehler: Tabelle 'lesson_statistic' existiert nicht!")
|
||||
return False
|
||||
|
||||
# Prüfe die Spalten der Tabelle
|
||||
cursor.execute("PRAGMA table_info(lesson_statistic)")
|
||||
columns = [column[1] for column in cursor.fetchall()]
|
||||
expected_columns = ['id', 'lesson_index', 'chars_per_minute', 'error_rate', 'wpm', 'created_at']
|
||||
|
||||
if not all(col in columns for col in expected_columns):
|
||||
print(f"Fehler: Tabelle hat nicht die erwarteten Spalten. Gefunden: {columns}")
|
||||
return False
|
||||
|
||||
print("✓ Datenbankstruktur ist korrekt")
|
||||
|
||||
# Testdaten einfügen
|
||||
test_lesson_index = 0
|
||||
test_chars_per_minute = 150
|
||||
test_error_rate = 5.5
|
||||
test_wpm = 30
|
||||
|
||||
cursor.execute('''
|
||||
INSERT INTO lesson_statistic (lesson_index, chars_per_minute, error_rate, wpm)
|
||||
VALUES (?, ?, ?, ?)
|
||||
''', (test_lesson_index, test_chars_per_minute, test_error_rate, test_wpm))
|
||||
|
||||
conn.commit()
|
||||
print("✓ Testdaten erfolgreich eingefügt")
|
||||
|
||||
# Testdaten auslesen und überprüfen
|
||||
cursor.execute('''
|
||||
SELECT lesson_index, chars_per_minute, error_rate, wpm
|
||||
FROM lesson_statistic
|
||||
WHERE lesson_index = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1
|
||||
''', (test_lesson_index,))
|
||||
|
||||
result = cursor.fetchone()
|
||||
|
||||
if not result:
|
||||
print("Fehler: Testdaten konnten nicht ausgelesen werden!")
|
||||
return False
|
||||
|
||||
stored_lesson_index, stored_chars_per_minute, stored_error_rate, stored_wpm = result
|
||||
|
||||
# Überprüfe die gespeicherten Werte
|
||||
if (stored_lesson_index == test_lesson_index and
|
||||
stored_chars_per_minute == test_chars_per_minute and
|
||||
stored_error_rate == test_error_rate and
|
||||
stored_wpm == test_wpm):
|
||||
print("✓ Alle Testwerte wurden korrekt gespeichert:")
|
||||
print(f" Lektion: {stored_lesson_index}")
|
||||
print(f" ZPM: {stored_chars_per_minute}")
|
||||
print(f" Fehlerrate: {stored_error_rate}%")
|
||||
print(f" WPM: {stored_wpm}")
|
||||
else:
|
||||
print("Fehler: Gespeicherte Werte stimmen nicht mit Testdaten überein!")
|
||||
print(f" Erwartet: Lektion={test_lesson_index}, ZPM={test_chars_per_minute}, "
|
||||
f"Fehlerrate={test_error_rate}, WPM={test_wpm}")
|
||||
print(f" Gespeichert: Lektion={stored_lesson_index}, ZPM={stored_chars_per_minute}, "
|
||||
f"Fehlerrate={stored_error_rate}, WPM={stored_wpm}")
|
||||
return False
|
||||
|
||||
# Teste die Berechnung von ZPM
|
||||
print("\nTeste ZPM-Berechnung...")
|
||||
|
||||
# Simuliere Tippen: 300 Zeichen in 60 Sekunden = 300 ZPM
|
||||
test_chars = 300
|
||||
test_time_seconds = 60
|
||||
calculated_zpm = (test_chars / test_time_seconds) * 60
|
||||
|
||||
if calculated_zpm == 300:
|
||||
print(f"✓ ZPM-Berechnung korrekt: {calculated_zpm} ZPM")
|
||||
else:
|
||||
print(f"Fehler: ZPM-Berechnung falsch. Erwartet: 300, Berechnet: {calculated_zpm}")
|
||||
return False
|
||||
|
||||
# Teste mehrere Datensätze für Zeitreihen
|
||||
print("\nTeste Zeitreihen-Funktionalität...")
|
||||
|
||||
# Füge mehrere Datensätze für dieselbe Lektion ein
|
||||
test_data_points = [
|
||||
(test_lesson_index, 100, 10.0, 20),
|
||||
(test_lesson_index, 150, 8.0, 25),
|
||||
(test_lesson_index, 200, 5.5, 30)
|
||||
]
|
||||
|
||||
for data_point in test_data_points:
|
||||
cursor.execute('''
|
||||
INSERT INTO lesson_statistic (lesson_index, chars_per_minute, error_rate, wpm)
|
||||
VALUES (?, ?, ?, ?)
|
||||
''', data_point)
|
||||
|
||||
conn.commit()
|
||||
|
||||
# Zähle die Datensätze für diese Lektion
|
||||
cursor.execute('''
|
||||
SELECT COUNT(*) FROM lesson_statistic WHERE lesson_index = ?
|
||||
''', (test_lesson_index,))
|
||||
|
||||
count = cursor.fetchone()[0]
|
||||
print(f"✓ Anzahl der Datensätze für Lektion {test_lesson_index}: {count}")
|
||||
|
||||
# Hole die Zeitreihe
|
||||
cursor.execute('''
|
||||
SELECT chars_per_minute, error_rate, wpm, created_at
|
||||
FROM lesson_statistic
|
||||
WHERE lesson_index = ?
|
||||
ORDER BY created_at ASC
|
||||
''', (test_lesson_index,))
|
||||
|
||||
time_series = cursor.fetchall()
|
||||
print(f"✓ Zeitreihe mit {len(time_series)} Datensätzen abgerufen")
|
||||
|
||||
# Zeige die Entwicklung
|
||||
print(" Entwicklung der ZPM-Werte:")
|
||||
for i, (zpm, error_rate, wpm, created_at) in enumerate(time_series[-5:]): # Letzte 5 Einträge
|
||||
print(f" Punkt {i+1}: ZPM={zpm}, Fehlerrate={error_rate}%, WPM={wpm}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Fehler während des Tests: {e}")
|
||||
return False
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Starte ZPM-Test...")
|
||||
print("=" * 50)
|
||||
|
||||
success = test_zpm_measurement_and_storage()
|
||||
|
||||
print("=" * 50)
|
||||
if success:
|
||||
print("🎉 Alle Tests erfolgreich! ZPM-Messung und -Speicherung funktionieren korrekt.")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("❌ Tests fehlgeschlagen!")
|
||||
sys.exit(1)
|
||||
Loading…
Add table
Add a link
Reference in a new issue