diff options
| -rw-r--r-- | config.php.sample | 22 | ||||
| -rw-r--r-- | index.css | 4 | ||||
| -rw-r--r-- | index.js | 60 | ||||
| -rw-r--r-- | index.php | 29 | ||||
| -rw-r--r-- | lade_daten.php | 62 | ||||
| -rw-r--r-- | sql/dummydata.sql | 4 |
6 files changed, 144 insertions, 37 deletions
diff --git a/config.php.sample b/config.php.sample index 2ca654f..3fed3bc 100644 --- a/config.php.sample +++ b/config.php.sample @@ -1,18 +1,22 @@ <?php $CONFIG = array( + /* Konfiguration für eine MySQL/MariaDB Datenbank */ 'dbtype' => 'mysql', 'dbname' => 'punktestand', 'dbhost' => 'localhost', 'dbuser' => 'user', 'dbpassword' => 'password', - 'bilder_pfad_format' => 'img/$SCHULJAHR/$JAHRGANG/' -); -/* Konfiguration für eine sqlite Datenbank - * $CONFIG = array( - * 'dbtype' => 'sqlite', - * 'dbname' => './punktestand.sqlite', - * 'bilder_pfad_format' => 'img/$SCHULJAHR/$JAHRGANG/' - * ); - */ + /* Konfiguration für eine sqlite Datenbank + * 'dbtype' => 'sqlite', + * 'dbname' => './punktestand.sqlite', + */ + + 'bilder_pfad_format' => 'img/$SCHULJAHR/$JAHRGANG/', + + /* Konfiguration für die Streak-Anzeige */ + 'streak_anzahl' => 5, + 'streak_ziel' => '🍰', + 'streak_ereignis' => 'Kuchenpunkt', +); ?> @@ -63,3 +63,7 @@ li.ereignis { margin-top: 2px; display: block; } + +td.streak { + text-align: center; +} @@ -25,11 +25,16 @@ * */ +// Observer der auf 'resize' events unseres Graphs reagiert und die +// Streak Anzeige gegebenfalls anpasst. +let graphResizeObserver; + // Zeichne das Diagramm und erstelle eine Liste aller Events, // wenn die Seite komplett geladen wurde window.onload = function() { zeichne(); zeichneBilder(); + zeichneStreak(); listeEvents(); } @@ -90,7 +95,7 @@ async function zeichneBilder() { // Die Url des Klassenbildes ist der Klassenbezeichner unter dem Bilderpfad let bildUrl = `${bilder_pfad}/${klasse.bezeichner}.png`; - // Teste ob das Bild existiert + // Teste, ob das Bild existiert let ok = (await fetch(bildUrl, {method: 'HEAD'})).ok; // Das Bild existiert nicht if (!ok) { continue; } @@ -119,8 +124,59 @@ async function zeichneBilder() { diagramm.update(); } +// Zeichne den Streak für alle Klassen. +// Die globale Variable daten.streakEreignisse muss definiert sein und ein Iterable +// aller Ereignisse, die für den Streak relevant sind, enthalten. +// Ereignisse ohne positives Delta beenden einen Streak. +function zeichneStreak() { + // Es gibt keine Streak-Ereignisse + if (!daten.streakEreignisse) { + return; + } + + // Variable um den laufenden Streak per Klasse zu bestimmen + let streaks = Object.fromEntries(daten.klassen.map(klasse => [klasse.bezeichner, 0])); + + for (let ereignis of daten.streakEreignisse) { + const klasse = `${ereignis.Jahrgang}${ereignis.Bezeichner}`; + + // Das Streak Ereignis bringt keine Punkte und unterbricht damit den Streak. + if (ereignis.Delta == 0) { + streaks[klasse] = 0; + // Erhöhe den Streak. + } else { + streaks[klasse] = streaks[klasse] + 1; + } + } + + let streakNodes = document.getElementById('streaks'); + for (const klasse of Object.keys(streaks)) { + const streak = streaks[klasse]; + let streakNode = document.createElement("td"); + streakNode.classList.add("streak"); + streakNode.innerHTML = `${streak}/${streak_anzahl} ${streak_ziel}`; + // Füge den Streak-Node der Klasse den Streak Nodes auf der Webseite hinzu. + streakNodes.appendChild(streakNode); + } + + const zeichenflaeche = document.getElementById('zeichenflaeche'); + let streakTabelle = document.getElementById('streaks-tabelle'); + // Setze die Breite passend zum Graphen + streakTabelle.setAttribute("style", `width:${zeichenflaeche.style.width}px`); + + // Erzeuge ein Observer der auf Größenveränderungen des Graphen reagiert. + graphResizeObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + // Passe die Breite unserer Streak-Tabelle an die des Graphen an. + streakTabelle.setAttribute("style", `width:${entry.contentRect.width}px`); + } + }); + + graphResizeObserver.observe(zeichenflaeche); +} + // Erzeuge eine Liste aller Ereignisse. -// Die globale Variable ereignisse muss definiert sein und ein Iterable aller +// Die globale Variable daten.ereignisse muss definiert sein und ein Iterable aller // Ereignisse, die angezeigt werden sollen, enthalten. function listeEvents() { let liste = document.getElementById("ereignisse"); @@ -40,18 +40,22 @@ // Bereite die Klassen und Ereignisse für Javascript vor $daten_json = json_encode($daten); - // Ersetze die $JAHRGANG und $SCHULJAHR Platzhalter durch die werte der Variablen + // Ersetze die $JAHRGANG und $SCHULJAHR Platzhalter durch die Werte der Variablen $bilder_pfad = str_replace( array('$JAHRGANG', '$SCHULJAHR'), array($JAHRGANG, $SCHULJAHR), $CONFIG['bilder_pfad_format']); $bilder_pfad_json = json_encode($bilder_pfad); + $streak_anzahl = json_encode($CONFIG['streak_anzahl'] ?? ''); + $streak_ziel = json_encode($CONFIG['streak_ziel'] ?? ''); // Schreibe den Javascript Code mit unseren Daten in die Webseite echo "<script>"; echo "const daten = $daten_json;\n"; - echo "const bilder_pfad = $bilder_pfad_json;"; + echo "const bilder_pfad = $bilder_pfad_json;\n"; + echo "const streak_anzahl = $streak_anzahl;\n"; + echo "const streak_ziel = $streak_ziel;\n"; echo "</script>"; ?> @@ -66,12 +70,21 @@ <!-- Container für den Inhalt des Punktestands --> <div id="content"> - <!-- Fortschrittsanzeige für das Zeichnen des Diagramms --> - <progress id="zeichnen" aria-label="Zeichnen" style="display: none;"></progress> - <!-- Container des Diagramms --> - <div id="zeichenort", aria-busy="true" aria-describedby="zeichnen"> - <!-- Das HTML Element das zum Zeichnen des Diagramms verwendet wird --> - <canvas id="zeichenflaeche"></canvas> + <div id="graphen"> + <!-- Fortschrittsanzeige für das Zeichnen des Diagramms --> + <progress id="zeichnen" aria-label="Zeichnen" style="display: none;"></progress> + <!-- Container des Diagramms --> + <div id="zeichenort", aria-busy="true" aria-describedby="zeichnen"> + <!-- Das HTML Element das zum Zeichnen des Diagramms verwendet wird --> + <canvas id="zeichenflaeche"></canvas> + </div> + <!-- Container fuer alle Streak-Anzeigen --> + <table id="streaks-tabelle" aria-label="Streak-Anzeiger jeder Klasse."> + <tbody> + <tr id="streaks"> + </tr> + </tbody> + </table> </div> <!-- Liste aller Ergeignisse --> diff --git a/lade_daten.php b/lade_daten.php index 67134be..ac15510 100644 --- a/lade_daten.php +++ b/lade_daten.php @@ -12,6 +12,24 @@ * punktestand. If not, see <https://www.gnu.org/licenses/>. */ +// PHP Klasse, die alle Daten einer Schulklasse enthält. +class Klasse { + public string $bezeichner; + public int $punkte; + public string $farbe; + + public function __construct(string $bezeichner, ?string $farbe) { + $this->bezeichner = $bezeichner; + $this->punkte = 0; + if (is_null($farbe)) { + // Die Standardfarbe ist ein leichtes Blau + $this->farbe = 'rgb(54, 162, 235)'; + } else { + $this->farbe = $farbe; + } + } +} + // Die Funktion lade_daten liefert die Klassen und ihren Punktestand, sowie alle // aufgetretenen Ereignisse in einem array zurück. function lade_daten($config, $schuljahr, $jahrgang): array { @@ -40,24 +58,6 @@ function lade_daten($config, $schuljahr, $jahrgang): array { // Die Verbindung war erfolgreich - // PHP Klasse, die alle Daten einer Schulklasse enthält. - class Klasse { - public string $bezeichner; - public int $punkte; - public string $farbe; - - public function __construct(string $bezeichner, ?string $farbe) { - $this->bezeichner = $bezeichner; - $this->punkte = 0; - if (is_null($farbe)) { - // Die Standardfarbe ist ein leichtes Blau - $this->farbe = 'rgb(54, 162, 235)'; - } else { - $this->farbe = $farbe; - } - } - } - // Bereite unsere Klassenabfrage vor. // Dies verhindert SQL-Injection Angriffe, da die Parameter Schuljahr und // Jahrgang über die URL kontrollierbar sind. @@ -84,6 +84,29 @@ function lade_daten($config, $schuljahr, $jahrgang): array { // Alle Ereignisse der ausgewählten Klassen $ereignisse = $ereignis_abfrage->fetchAll(); + // Deklaration der streak_ereignisse Variable, die nur unter bestimmten + // Umständen mit einem Wert gefüllt wird. Die Forward-Deklaration ermöglicht es + // die Variable danach zu benutzen unabhängig, davon ob sie mit einem sinnvollen + // Wert gefüllt wurde. + $streak_ereignisse = null; + $streak_ereignis_beschreibung = $config['streak_ereignis']; + if ($streak_ereignis_beschreibung != '') { + // Bereite die Streak-Ereignisabfrage vor. Sicherheitshinweis siehe: Klassenabfrage. + // Nur Ereignisse mit dem konfigurierten Namen werden ausgewählt. + // Zusätzlich werden nur Ereignisse mit einem Datum betrachtet, da zur Streak + // Berechnung die Ereignisse in eine zeitliche Reihenfolge gebracht werden + // müssen. + $streak_ereignis_abfrage = $verbindung->prepare('SELECT * FROM Ereignis WHERE Schuljahr = ? AND Jahrgang = ? AND Beschreibung = ? ORDER BY Datum ASC'); + $streak_ereignis_abfrage->bindValue(1, $schuljahr, PDO::PARAM_STR); + $streak_ereignis_abfrage->bindValue(2, $jahrgang, PDO::PARAM_INT); + $streak_ereignis_abfrage->bindValue(3, $streak_ereignis_beschreibung, PDO::PARAM_STR); + $streak_ereignis_abfrage->execute(); + + // Alle Streak-Ereignisse der ausgewählten Klassen + $streak_ereignisse = $streak_ereignis_abfrage->fetchAll(); + } + + // Schließe die Datenbank Verbindung $verbindung = null; @@ -96,6 +119,9 @@ function lade_daten($config, $schuljahr, $jahrgang): array { $daten = array(); $daten['klassen'] = array_values($klassen); $daten['ereignisse'] = $ereignisse; + if ($streak_ereignisse != null) { + $daten['streakEreignisse'] = $streak_ereignisse; + } return $daten; } ?> diff --git a/sql/dummydata.sql b/sql/dummydata.sql index 410a3ea..dd21854 100644 --- a/sql/dummydata.sql +++ b/sql/dummydata.sql @@ -1,3 +1,7 @@ INSERT INTO Ereignis (Jahrgang, Bezeichner, Schuljahr, Datum, Delta, Beschreibung) VALUES (7, 'a', '2526', '2025-09-21 10:32:17', 10, 'Aufpassen'); INSERT INTO Ereignis (Jahrgang, Bezeichner, Schuljahr, Delta, Beschreibung) VALUES (7, 'a', '2526', -2, 'Nerven'); INSERT INTO Ereignis (Jahrgang, Bezeichner, Schuljahr, Delta, Beschreibung) VALUES (7, 'b', '2526', 5, 'Quizzen'); +INSERT INTO Ereignis (Jahrgang, Bezeichner, Schuljahr, Delta, Beschreibung, Datum) VALUES (7, 'a', '2526', 1, 'Kuchenpunkt', '2025-10-12 13:20'); +INSERT INTO Ereignis (Jahrgang, Bezeichner, Schuljahr, Delta, Beschreibung, Datum) VALUES (7, 'a', '2526', 1, 'Kuchenpunkt', '2025-10-19 13:20'); +INSERT INTO Ereignis (Jahrgang, Bezeichner, Schuljahr, Delta, Beschreibung, Datum) VALUES (7, 'c', '2526', 1, 'Kuchenpunkt', '2025-10-12 13:20'); +INSERT INTO Ereignis (Jahrgang, Bezeichner, Schuljahr, Delta, Beschreibung, Datum) VALUES (7, 'c', '2526', 0, 'Kuchenpunkt', '2025-10-19 13:20'); |
