Inhalt
Aktueller Ordner:
duesseldorfer-schuelerinventar-visualstudio-clientREADME.md
DÜSK - WPF Desktop App für Windows
DÜSK ist eine professionelle Desktop-Anwendung für Windows zur Erfassung und Auswertung von Selbst- und Fremdeinschätzungen von Schülerkompetenzen. Die App wurde mit .NET 8.0 und WPF (Windows Presentation Foundation) entwickelt und bietet eine moderne, flüssige Benutzeroberfläche mit nativer Windows-Integration.
---
📋 Inhaltsverzeichnis
- Überblick
- Features
- Technologie-Stack
- Screenshots
- API-Dokumentation
- Datenbankstruktur
- Installation
- Projektstruktur
- Berechnungslogik
- Konfiguration
- Build & Deployment
- Entwicklung
- Fehlerbehandlung
---
🎯 Überblick
DÜSK (Düsseldorfer Schülerinventar) ist ein diagnostisches Instrument zur Erfassung von Schülerkompetenzen in sechs Bereichen:
| Bereich | Beschreibung |
|---------|--------------|
| 1. Arbeitsverhalten | Zuverlässigkeit, Arbeitstempo, Planung, Organisation |
| 2. Lernverhalten | Selbstständigkeit, Belastbarkeit, Konzentration, Merkfähigkeit |
| 3. Sozialverhalten | Teamfähigkeit, Hilfsbereitschaft, Kommunikation, Konfliktfähigkeit |
| 4. Fachkompetenz | Schreiben, Lesen, Mathematik, Naturwissenschaften, Fremdsprachen |
| 5. Personale Kompetenz | Kreativität, Problemlösung, Abstraktion, Reflexion |
| 6. Methodenkompetenz | Präsentation, PC-Kenntnisse, fächerübergreifendes Denken |
Die Anwendung ermöglicht den Vergleich von Selbsteinschätzung (SE) und Fremdeinschätzung (FE) mit Normtabellen für Hauptschulen (HS) und Förderschulen (FS).
---
✨ Features
Kernfunktionen
- ✅ Login/Logout mit persistenter Session-Speicherung
- ✅ CRUD-Operationen für Schülerprofile (Erstellen, Lesen, Aktualisieren, Löschen)
- ✅ 36 Items pro Einschätzung (4-stufige Likert-Skala: 1-4)
- ✅ Automatische Kompetenzberechnung aus 72 Items
- ✅ Normwertvergleich (HS/FS Normtabellen)
- ✅ Profilansicht mit Tabellen, interaktiven Diagrammen und Textinterpretation
Erweiterte Funktionen
- 📊 Zeitreihenanalyse für Gruppenentwicklung über Zeit
- 📈 Vergleichsdiagramme (SE vs. FE mit Pearson-Korrelation)
- 📐 Korrelationsberechnung mit statistischer Interpretation
- 📑 Exportfunktion (CSV, PDF in Entwicklung)
- 🔍 Such- und Filterfunktionen (nach Namen und Gruppen)
- 📁 Gruppenverwaltung (CRUD mit Umbenennung)
- 🌙 Modernes Fluent Design (Windows 11 optimiert)
- 💾 Persistente Speicherung (Session, Fensterposition)
- ⌨️ Tastatur-Shortcuts (Enter für Login, etc.)
- 🖥️ Native Windows-Integration (Taskbar, Fensterverwaltung)
---
🛠 Technologie-Stack
WPF App
| Komponente | Technologie | Version |
|------------|-------------|---------|
| Framework | .NET 8.0 | 8.0 |
| Sprache | C# | 12.0 |
| UI-Framework | WPF | - |
| HTTP-Client | HttpClient | .NET native |
| JSON-Parser | Newtonsoft.Json | 13.0.3 |
| Charts | LiveCharts.Wpf | 0.9.7 |
| Behaviors | Microsoft.Xaml.Behaviors.Wpf | 1.1.77 |
| Build-Tool | MSBuild | - |
| Mindest-Windows | Windows 10 | 1809+ |
Server (PHP API)
| Komponente | Technologie |
|------------|-------------|
| Backend | PHP 7.4+ |
| Datenbank | MySQL 5.7+ |
| Webserver | Apache/Nginx |
| Format | JSON |
---
📡 API-Dokumentation
Die REST-API ist unter
https://paul-koop.org/api/ verfügbar.Authentifizierung
#### POST
/api_login.php`json// Request
{
"username": "gast",
"password": "gast"
}
// Response (Success)
{
"success": true,
"userID": "12345",
"session": "abc123def456789"
}
// Response (Error)
{
"success": false,
"error": "Invalid credentials"
}
`Profile-Endpunkte
> Wichtig: Alle Profile-Endpunkte benötigen die Header:
> -
X-User-ID: Benutzer-ID aus Login> -
X-Session: Session-Token aus Login#### GET
/api_profiles.phpResponse: Array aller Profile des Benutzers
`json[
{
"profilID": "1",
"name": "Max Mustermann",
"gruppename": "Klasse 8a",
"gruppeID": "5",
"created_at": "2024-01-15 10:30:00",
"item1": 4, "item2": 3, ..., "item36": 2,
"feitem1": 3, "feitem2": 4, ..., "feitem36": 3
}
]
`#### GET
/api_profiles.php?id={profileId}Response: Einzelnes Profil
#### POST
/api_profiles.phpRequest Body: Vollständiges Profil-Objekt (alle 72 Items)
Response:
200 OK bei Erfolg#### PUT
/api_profiles.phpRequest Body: Aktualisiertes Profil-Objekt
Response:
200 OK bei Erfolg#### DELETE
/api_profiles.php?id={profileId}Response:
200 OK bei ErfolgGruppen-Endpunkte
#### GET
/api_groups.phpResponse:
`json[
{"gruppeID": 1, "name": "Klasse 8a"},
{"gruppeID": 2, "name": "Klasse 8b"},
{"gruppeID": 3, "name": "Klasse 9a"}
]
`#### POST
/api_groups.phpRequest Body:
{"name": "Neue Gruppe"}Response:
200 OK bei Erfolg#### DELETE
/api_groups.php?id={groupId}Response:
200 OK bei Erfolg---
🗄 Datenbankstruktur
ER-Diagramm
`┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ users │ │ groups │ │ profiles │
├─────────────┤ ├─────────────┤ ├─────────────┤
│ userID (PK) │────<│ userID (FK) │ │ profilID(PK)│
│ username │ │ gruppeID(PK)│<────│ userID (FK) │
│ password │ │ name │ │ gruppeID(FK)│
│ session │ │ created_at │ │ name │
│ created_at │ └─────────────┘ │ item1-36 │
└─────────────┘ │ feitem1-36 │
│ created_at │
│ updated_at │
└─────────────┘
`SQL-Schema
`sql-- Users Tabelle
CREATE TABLE users (
userID INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
session_token VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Groups Tabelle
CREATE TABLE groups (
gruppeID INT AUTO_INCREMENT PRIMARY KEY,
userID INT NOT NULL,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (userID) REFERENCES users(userID) ON DELETE CASCADE,
UNIQUE KEY unique_group_per_user (userID, name)
);
-- Profiles Tabelle
CREATE TABLE profiles (
profilID INT AUTO_INCREMENT PRIMARY KEY,
userID INT NOT NULL,
name VARCHAR(255) NOT NULL,
gruppeID INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- 36 SE-Items (Werte 1-4)
item1 TINYINT DEFAULT 2, item2 TINYINT DEFAULT 2, ..., item36 TINYINT DEFAULT 2,
-- 36 FE-Items (Werte 1-4)
feitem1 TINYINT DEFAULT 2, feitem2 TINYINT DEFAULT 2, ..., feitem36 TINYINT DEFAULT 2,
FOREIGN KEY (userID) REFERENCES users(userID) ON DELETE CASCADE,
FOREIGN KEY (gruppeID) REFERENCES groups(gruppeID) ON DELETE SET NULL
);
`---
💻 Installation
Voraussetzungen
- Windows 10 oder Windows 11 (64-Bit)
- .NET 8.0 Desktop Runtime (Download)
- Visual Studio 2022 (für Entwicklung, mindestens Community Edition)
Schritt-für-Schritt Installation
`bash1. Repository klonen
git clone https://github.com/yourusername/duesk-wpf.git
cd duesk-wpf
2. Projekt in Visual Studio öffnen
Doppelklick auf DueskWPF.sln oder DueskWPF.csproj
3. NuGet-Pakete wiederherstellen
dotnet restore
4. App starten (Debug)
dotnet run
5. Release-Build erstellen
dotnet publish -c Release -o ./publish
`Schnellstart mit der Batch-Datei (Windows)
`batchProjektstruktur erstellen
create_wpf_project.bat
In das Projekt wechseln
cd duesk-wpf
Build ausführen
dotnet build
App starten
dotnet run
`---
📁 Projektstruktur
`duesk-wpf/
├── DueskWPF.csproj # Projektdatei mit NuGet-Abhängigkeiten
├── App.xaml # Anwendungsressourcen & Styles
├── App.xaml.cs # App-Lifecycle (Startup, Exit)
├── Models/
│ ├── LoginResponse.cs # API Response Model für Login
│ ├── Profile.cs # Profil-Datenmodell (72 Items)
│ ├── Group.cs # Gruppen-Datenmodell
│ └── Norms.cs # Normtabellen & Konstanten (HS/FS)
├── Services/
│ ├── SessionManager.cs # Session & persistente Speicherung
│ ├── ApiService.cs # HTTP-API-Kommunikation
│ └── Calculator.cs # Berechnungslogik (Sums, Korrelation)
├── Views/
│ ├── LoginWindow.xaml # Login-Fenster
│ ├── LoginWindow.xaml.cs
│ ├── MainWindow.xaml # Hauptfenster mit Sidebar & Toolbar
│ ├── MainWindow.xaml.cs
│ ├── ProfileDetailWindow.xaml # Profildetails mit Charts & Tabs
│ ├── ProfileDetailWindow.xaml.cs
│ ├── ProfileEditWindow.xaml # Profil erstellen/bearbeiten
│ ├── ProfileEditWindow.xaml.cs
│ ├── GroupManagerWindow.xaml # Gruppenverwaltung (CRUD)
│ ├── GroupManagerWindow.xaml.cs
│ ├── TimeSeriesWindow.xaml # Zeitreihenanalyse mit Chart
│ └── TimeSeriesWindow.xaml.cs
├── Converters/
│ └── BoolToVisibilityConverter.cs # Wertkonverter für UI
└── Resources/
└── Styles.xaml # Globale UI-Styles (Buttons, TextBoxes)
`---
🧮 Berechnungslogik
Item-zu-Kompetenz-Zuordnung
`csharp// Kompetenz 1: Arbeitsverhalten (Items 1-10)
for (var i = 0; i < 10; i++) sums[1] += items[i];
// Kompetenz 2: Lernverhalten (Items 11-20)
for (var i = 10; i < 20; i++) sums[2] += items[i];
// Kompetenz 3: Sozialverhalten (Items 21-28 + Items 9-10)
for (var i = 20; i < 28; i++) sums[3] += items[i];
sums[3] += items[8] + items[9];
// Kompetenz 4: Fachkompetenz (Items 29-36)
for (var i = 28; i < 36; i++) sums[4] += items[i];
// Kompetenz 5: Personale Kompetenz (spezifische Items)
sums[5] = items[0] + items[1] + items[5] + items[6] + items[7] +
items[8] + items[9] + items[11] + items[12] + items[13] + items[14];
// Kompetenz 6: Methodenkompetenz (spezifische Items)
sums[6] = items[2] + items[3] + items[4] + items[8] + items[9] +
items[10] + items[16] + items[17];
`Profilwertberechnung
`csharppublic static int[] CalculateProfileValues(int[] sums, double[][] norm)
{
var values = new int[6];
for (var k = 1; k <= 6; k++)
{
var value = 5;
for (var p = 0; p < 5; p++)
{
if (sums[k] < norm[k - 1][p])
{
value = p + 1;
break;
}
}
values[k - 1] = value;
}
return values;
}
`Korrelationsberechnung (Pearson)
`csharppublic static double CalculateCorrelation(int[] seValues, int[] feValues)
{
var n = seValues.Length;
double sumSE = 0, sumFE = 0, sumSEFE = 0, sumSE2 = 0, sumFE2 = 0;
for (var i = 0; i < n; i++)
{
sumSE += seValues[i];
sumFE += feValues[i];
sumSEFE += seValues[i] * feValues[i];
sumSE2 += seValues[i] * seValues[i];
sumFE2 += feValues[i] * feValues[i];
}
var numerator = n sumSEFE - sumSE sumFE;
var denominator = Math.Sqrt((n sumSE2 - sumSE sumSE) *
(n sumFE2 - sumFE sumFE));
return Math.Abs(denominator) < 0.0001 ? 0 : numerator / denominator;
}
`Normtabellen (Vollständig)
| Kompetenz | HS SE Grenzwerte | HS FE Grenzwerte |
|-----------|------------------|------------------|
| Arbeitsverhalten | 21.33, 25.33, 29.33, 33.32, 37.32 | 12.66, 18.16, 23.66, 29.16, 34.66 |
| Lernverhalten | 20.87, 24.95, 29.03, 33.13, 37.18 | 13.33, 18.42, 23.51, 28.60, 33.69 |
| Sozialverhalten | 17.93, 21.37, 24.80, 28.23, 31.67 | 10.75, 15.41, 20.07, 24.73, 29.39 |
| Fachkompetenz | 13.98, 17.71, 21.44, 25.17, 28.90 | 14.22, 15.30, 16.38, 17.46, 18.54 |
| Personale Kompetenz | 24.60, 28.55, 33.04, 37.53, 42.01 | 14.12, 20.21, 26.30, 32.39, 38.48 |
| Methodenkompetenz | 15.53, 18.97, 22.40, 25.83, 29.27 | 10.53, 14.51, 18.49, 22.47, 26.45 |
Für FS (Förderschule) existieren separate Normtabellen.
---
⚙️ Konfiguration
API Base URL ändern
`csharp// Services/ApiService.cs
private const string BaseUrl = "https://your-server.com/api/";
`Session-Speicherort
Die Session wird als
session.json im Ausführungsverzeichnis gespeichert.Fensterposition speichern
Die Fenstergröße und -position wird automatisch im
session.json gespeichert.---
🚀 Build & Deployment
Entwicklung
`bashBuild ausführen
dotnet build
App starten
dotnet run
Release-Build
dotnet build -c Release
Publish als Einzeldatei
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o ./publish
`Installer erstellen
Für die Erstellung eines Setup-Installers kann z.B. Inno Setup oder das Visual Studio Installer Projekt verwendet werden.
Systemanforderungen
- Betriebssystem: Windows 10 / Windows 11 (64-Bit)
- .NET Runtime: .NET 8.0 Desktop Runtime
- RAM: mindestens 256 MB
- Festplatte: etwa 50 MB
---
⌨️ Tastatur-Shortcuts
| Shortcut | Aktion |
|----------|--------|
|
Enter | Login ausführen (im Login-Fenster) ||
F5 | Profile aktualisieren ||
Ctrl + N | Neues Profil (geplant) |---
🐛 Fehlerbehandlung
HTTP-Statuscodes
| Code | Bedeutung | Behandlung |
|------|-----------|------------|
| 200 | Erfolg | Daten verarbeiten |
| 400 | Bad Request | Validierungsfehler prüfen |
| 401 | Unauthorized | Neu anmelden |
| 403 | Forbidden | Berechtigung prüfen |
| 404 | Not Found | Ressource existiert nicht |
| 500 | Server Error | Erneut versuchen, Support kontaktieren |
Typische Fehler und Lösungen
`csharptry
{
var profiles = await _apiService.GetProfilesAsync();
}
catch (HttpRequestException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
// Session abgelaufen - erneut anmelden
SessionManager.Instance.Clear();
var loginWindow = new LoginWindow();
loginWindow.Show();
Close();
}
else
{
MessageBox.Show($"Fehler: {ex.Message}", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (TaskCanceledException)
{
MessageBox.Show("Verbindung zeitüberschritten", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (Exception ex)
{
MessageBox.Show($"Unbekannter Fehler: {ex.Message}", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
`---
🔄 Vergleich der Implementierungen
| Feature | WPF (.NET) | Electron | Flutter | React Native | Swift (iOS) | Java (Swing) |
|---------|------------|----------|---------|--------------|-------------|--------------|
| Windows | ✅ Exzellent | ✅ Gut | ✅ Gut | ❌ | ❌ | ✅ Gut |
| macOS | ❌ | ✅ Gut | ✅ Gut | ❌ | ✅ Exzellent | ✅ Gut |
| Linux | ❌ | ✅ Gut | ✅ Gut | ❌ | ❌ | ✅ Gut |
| iOS | ❌ | ❌ | ✅ Gut | ✅ Gut | ✅ Exzellent | ❌ |
| Android | ❌ | ❌ | ✅ Gut | ✅ Gut | ❌ | ❌ |
| Web | ❌ | ❌ | ✅ Gut | ❌ | ❌ | ❌ |
| Performance | Exzellent | Gut | Exzellent | Gut | Exzellent | Exzellent |
| Installationsgröße | ~15 MB | ~80 MB | ~25 MB | ~30 MB | ~10 MB | ~15 MB |
| Windows-Integration | Exzellent | Gut | Mittel | - | - | Gut |
| Lernkurve | Mittel | Gering | Mittel | Gering | Hoch | Mittel |