aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2025-10-19 15:04:15 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2025-10-19 15:04:15 +0200
commit9f7b24bb6177d65e0b9d1b77a578c50af9c7f493 (patch)
tree70bb59ca2e5f4fd420f6638d237cbbe6a45ac31c
parent64e898aa9b0a7faf1184ded80636b28fd0c32e88 (diff)
downloadpunktestand-9f7b24bb6177d65e0b9d1b77a578c50af9c7f493.tar.gz
punktestand-9f7b24bb6177d65e0b9d1b77a578c50af9c7f493.zip
unterstützte konfigurierbare "Streaks"
Ein Streak ist ein Zeitraum der zählt wie viele gleiche Ereignisse in zeitlicher Folge aufgetreten sind. Ein Streak wird durch ein nicht positives Ereignis beendet.
-rw-r--r--config.php.sample22
-rw-r--r--index.css4
-rw-r--r--index.js60
-rw-r--r--index.php29
-rw-r--r--lade_daten.php62
-rw-r--r--sql/dummydata.sql4
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' => '&#x1F370',
+ 'streak_ereignis' => 'Kuchenpunkt',
+);
?>
diff --git a/index.css b/index.css
index 31b82e5..fd4eba9 100644
--- a/index.css
+++ b/index.css
@@ -63,3 +63,7 @@ li.ereignis {
margin-top: 2px;
display: block;
}
+
+td.streak {
+ text-align: center;
+}
diff --git a/index.js b/index.js
index 8f145dc..55ce7b0 100644
--- a/index.js
+++ b/index.js
@@ -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");
diff --git a/index.php b/index.php
index 8f8e101..fa1bb83 100644
--- a/index.php
+++ b/index.php
@@ -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');