Commit 09079756 authored by Robert Czechowski's avatar Robert Czechowski

Merge branch 'v1.9' into deploy

parents 5a42d3a7 7a38daee
Pipeline #1188 passed with stages
in 21 minutes and 22 seconds
......@@ -8,7 +8,7 @@ stages:
variables:
CARGO_HOME: $CI_PROJECT_DIR/cargo
APT_CACHE_DIR: $CI_PROJECT_DIR/apt
CACHE_COMPRESSION_LEVEL: 0
CACHE_COMPRESSION_LEVEL: fastest
......
This diff is collapsed.
[package]
version = "1.8.0"
version = "1.9.0"
name = "medal"
authors = ["Robert Czechowski <czechowski@bwinf.de>", "Daniel Brüning <bruening@bwinf.de>"]
......@@ -10,7 +10,7 @@ readme = "README.md"
license = "AGPL-3.0-or-later"
[badges]
maintenance = { status = "active-developed" }
maintenance = { status = "actively-developed" }
gitlab = { repository = "https://git.bwinf.de/bwinf/medal", branch = "master" }
is-it-maintained-open-issues = { repository = "https://git.bwinf.de/bwinf/medal" }
is-it-maintained-issue-resolution = { repository = "https://git.bwinf.de/bwinf/medal" }
......
......@@ -20,10 +20,10 @@ format: src/db_conn_postgres.rs
cargo +nightly fmt
clippy: src/db_conn_postgres.rs
cargo clippy --all-targets --features 'complete debug' -- -D warnings -A clippy::type-complexity -A clippy::option-map-unit-fn -A clippy::len-zero -A clippy::option-as-ref-deref -A clippy::or-fun-call -A clippy::comparison-to-empty -A clippy::result-unit-err -A clippy::unnecessary-wraps
cargo clippy --all-targets --features 'complete debug' -- -D warnings -A clippy::type-complexity -A clippy::option-map-unit-fn -A clippy::len-zero -A clippy::option-as-ref-deref -A clippy::or-fun-call -A clippy::comparison-to-empty -A clippy::result-unit-err -A clippy::unnecessary-wraps -A clippy::vec-init-then-push
src/db_conn_postgres.rs: src/db_conn_warning_header.txt src/db_conn_sqlite_new.header.rs src/db_conn_postgres.header.rs src/db_conn.base.rs
cd src; ./generate_connectors.sh
tools/generate_connectors.sh
doc: src/db_conn_postgres.rs
cargo doc --no-deps
......@@ -32,3 +32,6 @@ doc: src/db_conn_postgres.rs
grcov: src/db_conn_postgres.rs
CARGO_INCREMENTAL=0 RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" RUSTDOCFLAGS="-Cpanic=abort" cargo +nightly test
grcov ./target/debug/ -s . -t html --llvm --branch --ignore-not-existing -o ./target/debug/coverage/
hooks:
tools/install-hooks.sh
# The Medal Contest Platform ![Logo](static/images/medal_logo_small.png)
# ![Logo](static/images/medal_logo_small.png) The Medal Contest Platform
[![crates.io](https://img.shields.io/crates/v/medal?color=orange)](https://crates.io/crates/medal)
[![documentation](https://img.shields.io/crates/v/medal?label=docs)](https://jim.test.bwinf.de/doc/medal/)
......@@ -9,8 +9,18 @@ Medal is a small platform for in-browser running contest written in rust.
It is designed for the German "Jugendwettbewerb Informatik", a computer science contest with tasks using Google Blockly as a programming language-replacement.
Try it out on https://jwinf.de/!
**Try it out on [jwinf.de](https://jwinf.de/)**
[Benutzerhandbuch (German manual)](manuals/Benutzerhandbuch.md)
## Screenshots
[![Index](static/images/screenshots/tn_index.png)](static/images/screenshots/index.png)
[![Index](static/images/screenshots/tn_training_list.png)](static/images/screenshots/training_list.png)
[![Index](static/images/screenshots/tn_training.png)](static/images/screenshots/training.png)
[![Index](static/images/screenshots/tn_task.png)](static/images/screenshots/task.png)
[![Index](static/images/screenshots/tn_task_solved.png)](static/images/screenshots/task_solved.png)
[![Index](static/images/screenshots/tn_group_list.png)](static/images/screenshots/group_list.png)
## Translation
......
Handbuch zur Bedienung der JwInf-Plattform "Medal"
==================================================
# Handbuch zur Bedienung der JwInf-Plattform "Medal"
Herzlichen Glückwunsch zur Wahl der JwInf-Plattform "Medal". Sie haben sich für
ein erstklassiges und stabiles Produkt entschieden. Bitte lesen Sie das folgende
Handbuch aufmerksam durch, damit Sie noch lange ein angenehmes Nutzererlebnis
mit der Plattform "Medal" haben.
#### Inhalt
* [Bedienung für Teilnehmerinnen und Teilnehmer](#bedienung-f%C3%BCr-teilnehmerinnen-und-teilnehmer)
* [Bedienung für Lehrkräfte und andere Betreuer](#bedienung-f%C3%BCr-lehrkr%C3%A4fte-und-andere-betreuer)
* [Bedienung für Administratoren](#bedienung-f%C3%BCr-administratoren)
* [Troubleshooting](#troubleshooting)
Bedienung für Teilnehmerinnen und Teilnehmer
-------------------------------------------
......@@ -17,80 +22,112 @@ Es gibt drei verschiedene Möglichkeiten sich zur Teilnahme am Jugendwettbewerb
Gehe auf https://jwinf.de/. Gib den Logincode oben rechts im Feld `Gruppencode oder Logincode` ein. Drücke [Enter] oder klicke auf `Login`.
#### Einloggen mit Gruppencode
Gehe auf https://jwinf.de/. Gib den Gruppencode oben rechts im Feld `Gruppencode oder Logincode` ein. Drücke [Enter] oder klicke auf `Login`.
Verfolständige nun dein Profil und klicke auf `Speichern`.
Gehe auf https://jwinf.de/. Gib den Gruppencode oben rechts im Feld `Gruppencode oder Logincode` ein. Drücke [Enter] oder klicke auf `Login`. Vervollständige nun dein Profil und klicke auf `Speichern`.
#### Einloggen mit BWINF-Account
Gehe auf https://jwinf.de/. Klicke oben rechts auf `BWINF-Login / Lehrer-Login`. Logge dich mit deinen BWINF-Accountdaten ein.
### Am Wettbewerb teilnehmen
[…]
Logge dich zunächst ein. Gehe dann auf die Fläche `Jugendwettbewerb Informatik – Aktuelle Wettbewerbe`. Wähle den Wettbewerb aus.
Du siehst nun einen gelben Button mit der Aufschrift `Jetzt starten!`. Sobald du dieses Button drückst, läuft die Wettbewerbszeit ab. Der Wettbewerb kann nach dem Starten nicht mehr pausiert werden!
Wenn du den Wettbewerb nicht starten kannst, kann das die folgenden Ursachen haben:
* Du bist nicht eingeloggt: Du musst dich einloggen um Wettbewerbe starten zu können.
* Der Wettbewerb hat noch nicht begonnen. Du kannst den Wettbewerb erst starten, wenn er begonnen hat. Die Zeit bis zum Begin wird angezeigt.
* Der Wettbewerb ist schon beendet. Du kannst nicht mehr am Wettbewerb teilnehmen.
* Deine Jahrgangsstufe entspricht nicht der Altersgruppe für die dieser Wettbewerb eingestellt ist. Wenn deine im Profil eingestellte Jahrgangsstufe korrekt ist, wähle einen anderen Wettbewerb aus, dessen Altersgruppe zu dir passt. Wenn deine im Profil eingestellte Jahrgangsstufe falsch ist, kannst du sie im Profil anpassen.
### Das Profil
Das Profil enthält deine persönlichen zum Account gehörenden Daten. Du kannst dort deinen Namen und deine Jahrgangsstufe angeben und deinen Logincode einsehen. Außerdem enthält dein Profil eine Liste aller Wettbewerbe und Trainingsbereiche, an denen du teilgenommen hast.
#### Profil aufrufen
Klicke oben rechts auf `Profil`.
Klicke oben rechts auf `Profil`. Du kannst dein Profil jederzeit anpassen.
#### Wettbewerbsergebnisse einsehen
Gehe auf dein Profil. Unten findest du eine Liste der Wettbewerbe. Wähle den passenden Wettbewerb aus. Du siehst nun dein Ergebnis.
### FAQ
##### Ich habe meinen Logincode nicht aufgeschrieben. Kann ich ihn noch einsehen?
Ja. Gehe dazu auf dein Profil. Dort wird der Logincode angezeigt.
##### Ich habe meinen Logincode nicht aufgeschrieben und mich bereits ausgeloggt. Kann ich mich nochmal mit dem Gruppencode einloggen.
Nein. Beim einloggen mit dem Gruppencode wird ein neuer Account angelegt. Um deinen Logincode zu erfahren, frage deinen Lehrer.
Nein. Beim Einloggen mit dem Gruppencode wird ein neuer Account angelegt. Um deinen Logincode zu erfahren, frage deine Lehrkraft.
Bedienung für Lehrer und andere Betreuer
----------------------------------------
## Bedienung für Lehrkräfte und andere Betreuer
### Einloggen
#### Einloggen auf jwinf.de
Gehen Sie auf https://jwinf.de/. Klicken Sie oben rechts auf `BWINF-Login / Lehrer-Login`. Loggen Sie sich mit Ihren BWINF-Accountdaten ein. Wählen Sie im folgenden Menü Ihre Schule aus, mit der Sie sich für diese Sitzung anmelden möchten.
#### Einloggen über login.bwinf.de (funktioniert zur Zeit noch nicht)
#### Einloggen über login.bwinf.de
Gehen Sie auf https://login.bwinf.de/ und loggen Sie sich ein. Wählen Sie oben in der Navigation `Wettbewerbe` und dann `Jugendwettbewerb Informatik` aus. Klicken Sie auf den ersten Link. Wählen Sie im folgenden Menü Ihre Schule aus, mit der Sie sich für diese Sitzung anmelden möchten.
### Gruppenverwaltung
In der Gruppenverwaltung können Sie neue Gruppen anlegen, bestehende Gruppen anpassen und die Wettbewerbsergebnisse einsehen. Sie können so viele Gruppen anlegen, wie Sie möchten.
#### Gruppenverwaltung aufrufen
[…]
Klicken Sie oben rechts auf `Gruppenverwaltung`.
#### Unterschied zwischen Gruppencode und Logincode
[…]
Sobald Sie eine Gruppe angelegt haben, erhalten Sie einen Gruppencode. Diesen müssen Sie an die Schülerinnen und Schüler weiter geben. Sobald diese sich mit dem Gruppencode registriert haben, sehen Sie die Angelmeldeten in ihrer Gruppenübersicht.
Wenn sich die Schülerinnen und Schüler mit dem Gruppencode registrieren, müssen sie ihre Daten eingeben und erhalten einen Logincode. Mit diesem Logincode melden sich die Schülerinnen und Schüler für die Teilnahme an den Wettbewerben (Probewettbewerb, 1. Runde, 2. Runde) an.
#### Neue Gruppe erstellen
[…]
Um eine neue Gruppe zu erstellen klicken Sie auf `Gruppenverwaltung`. Geben Sie im Feld `Gruppenname` den Namen der Gruppe an. Das Feld `Klassen-/Kursbezeichnung` ist optional. Es dient beim Ausdrucken der Urkunden zur richtigen Sortierung. Klicken Sie dann auf das Feld `Neue Gruppe anlegen`. Sie wechseln in die Gruppenansicht. Dort sehen Sie dann den Gruppencode, den Sie an Ihre Teilnehmerinnen und Teilnehmer weiter geben.
#### Neue Gruppe mit fertigen Accounts per CSV-Upload erstellen
[…]
Sie können die Gruppe auch mit Hilfe einer CSV-Datei erstellen. Gehen Sie dazu in die `Gruppenübersicht` und klicken Sie auf den Link `CSV Upload`. Es öffnet sich eine neue Seite, in der Sie die Datei hochladen können. Sie erhalten dann für jeden einzelnen Teilnehmer direkt den Logincode.
Die CSV-Datei muss die folgenden Kriterien erfüllen:
* Die CSV-Datei muss mit Komma- oder Tab-getrennt sein
* Die Datei muss in UTF-8 (Unicode UTF-8) kodiert sein
* Die Datei muss mindestens vier Spalten enthalten. Alle weiteren Spalten werden ignoriert:
* der Name der Gruppe, (es können mehrere Gruppen in einer Datei definert sein)
* die Jahrgangsstufe des Teilnehmers,
* den Vorname des Teilnehmers und
* den Nachname des Teilnehmers
Beispiel-CSV-Datei:
```
Gruppenname,Stufe,Vorname,Nachname
7a,7,Gabi,Musterfrau
7a,7,Max,Mustermann
Info19,12,Ferdinand,Fallbeispiel
```
Dieses Beispiel legt zwei Gruppen an: `7a` und `Info19`. In der ersten Gruppe werden zwei Nutzer angelegt, in der zweiten Gruppe wird ein Nutzer angelegt.
#### Gruppe anzeigen
[…]
Gehen Sie in die `Gruppenverwaltung`. Unten auf der Seite unter `Meine verwalteten Gruppen` sehen Sie alle Ihre Gruppen. Klicken Sie auf den Namen einer Gruppe, um die Gruppe zu öffnen. Sie sehen dann alle Mitglieder der Gruppe und den Logincode der einzelnen Teilnehmerinnen und Teilnehmer. Dort haben Sie auch die Möglichkeit die Daten wie Name, Jahrgangsstufe und Geschlecht zu ändern.
#### Mitgliederliste von Gruppe herunterladen
[…]
Sie können die Mitgliederliste ihrer Gruppen als CSV-Datei herunterladen. Gehen Sie dazu in die `Gruppenverwaltung` zur Übersicht der Gruppen. In der Spalte Download finden Sie einen Link mit dem Sie die Datei herunterladen können. Oder gehen Sie in eine Ihrer Gruppen. Dort finden Sie auch den Link zum herunterladen der Datei.
### Wettbewerbsergebnisse
#### Wettbewerbsergebnisse einsehen
[…]
Klicken Sie oben rechts auf `Gruppenverwaltung`. Klicken Sie dort auf `Wettbewerbsergebnisse einsehen.` Wählen Sie dann den Wettbewerb aus, bei dem Sie die Ergebnisse Ihrer Teilnehmerinnen und Teilnehmer einsehen möchten. Sie erhalten eine Übersicht.
Alternativ wählen Sie auf der Startseite den Wettbewerb oder die Übungsaufgabe aus, von dem Sie die Ergebnisse einsehen wollen. Klicken Sie dann auf den blauen Kasten `Gruppenergebnisse`.
#### Wettbewerbsergebnisse herunterladen
[…]
Wenn Sie in der Ergnisübersicht eines Wettbewerbs sind, finden Sie einen Link, mit dem Sie das Ergebnis als CSV-Datei herunterladen können.
### FAQ
##### Ein Schüler hat seinen Logincode nicht aufgeschrieben. Kann ich ihn für ihn einsehen?
Ja. Gehen Sie dazu in die Gruppenverwaltung und lassen Sie sich die entsprechende Gruppe anzeigen. In der Ansicht wird neben jedem Schüler sein Logincode angezeigt.
##### Kann ich den Schülern fertige Zugangsdaten auf Papier ausdrucken?
Ja. Erstellen Sie eine neue Gruppe mit allen Schülern per CSV-Upload (s.o.). Dabei wird für jeden Schüler ein individueller Logincode erstellt.
##### Ein/e SchülerIn hat den Logincode nicht aufgeschrieben. Kann ich ihn für ihn einsehen?
Ja. Gehen Sie dazu in die `Gruppenverwaltung` und lassen Sie sich die entsprechende Gruppe anzeigen. In der Ansicht wird neben jeder Teilnemerin / jedem Teilnehmer sein Logincode angezeigt.
##### Kann ich den Teilnehmerinnen und Teilnehmern fertige Zugangsdaten auf Papier ausdrucken?
Ja. Erstellen Sie eine neue Gruppe mit allen Teilnehmerinnen und Teilnehmern per CSV-Upload (s.o.). Dabei wird für jeden Schüler ein individueller Logincode erstellt.
Wenn Sie sich dann die Mitgliederliste von Gruppe herunterladen, haben Sie eine Datei, die zu jedem Namen den Logincode auflistet. Diese können Sie nach belieben formatieren, ausdrucken und ausschneiden.
Bedienung für Administratoren
-----------------------------
## Bedienung für Administratoren
### Einloggen
Gehen Sie auf https://jwinf.de/. Klicken Sie oben rechts auf `BWINF-Login / Lehrer-Login`. Loggen Sie sich mit Ihren BWINF-Accountdaten ein.
......@@ -100,18 +137,55 @@ Klicken Sie oben rechts auf `Administration` oder gehen Sie direkt auf https://j
Auf der Administrationsseite können Sie nach Benutzern und Gruppen suchen. Dafür stehen die folgenden Sucharten zur Verfügung:
1. Suche nach Namen des Benutzers
2. Suche nach Logincode des Benutzers
3. Suche nach Gruppencode der Gruppe
4. Suche nach Nutzer-ID des Benutzers
5. Suche nach PMS-ID des Benutzers
1. Suche nach Namen des Benutzers
2. Suche nach Logincode des Benutzers
3. Suche nach Gruppencode der Gruppe
4. Suche nach Nutzer-ID des Benutzers
5. Suche nach PMS-ID des Benutzers
Bei der Suche nach dem Namen des Benutzers lässt sich das Zeichen `%` als Wildcard einsetzen. Um nach allen Namen zu Suchen, die auf "ter" enden, können Sie also `%ter` eingeben. Um nach allen Namen zu suchen, die ein `aus` enthalten, können Sie `%aus%` eingeben. Die Suche nach Namen ist Case-Sensitive, unterscheidet also Groß-und-kleinschreibung.
Bei der Suche nach dem Namen des Benutzers lässt sich das Zeichen `%` als Wildcard einsetzen. Um nach allen Namen zu suchen, die auf "ter" enden, können Sie also `%ter` eingeben. Um nach allen Namen zu suchen, die ein `aus` enthalten, können Sie `%aus%` eingeben. Die Suche nach Namen ist Case-Sensitive, unterscheidet also Groß-und-kleinschreibung.
Um nach eine Gruppe zu suchen, denen Gruppencode Sie nicht kennen, können Sie entweder nach einem Teilnehmer der Gruppe suchen oder nach dem Ersteller der Gruppe. Von dort aus können Sie dann alle Gruppen sehen, die der Nutzer erstellt hat oder die Gruppe in der er Mitglied ist.
Um nach eine Gruppe zu suchen, deren Gruppencode Sie nicht kennen, können Sie entweder nach einem Teilnehmer der Gruppe suchen oder nach dem Ersteller der Gruppe. Von dort aus können Sie dann alle Gruppen sehen, die der Nutzer erstellt hat oder die Gruppe in der er Mitglied ist.
Analog können Sie auch nach einem Benutzer suchen, in dem Sie nach einem Schüler des Lehrers suchen oder andersherum nach dem Lehrer des Schülers.
Die Suche liefert maximal 30 Ergebnisse. Sollten mehr als 30 Treffer zu Ihrer Suche gefunden werden, müssen Sie Ihre Suche verfeinern.
### Benutzer anzeigen
Suchen sie nach dem Nutzer unter Angabe des Namen, des Logincode, der Nutzer-ID oder der PMS-ID des Benutzers. Wählen Sie den entsprechenden Nutzer. Alternativ ist eine Suche über die Gruppe möglich (s. o.).
Die folgenden Informationen zu Benutzern werden angezeigt:
* die Nutzer-Id,
* der Vor- und Nachname,
* ggf. der Logincode
* ggf. der Status als Lehrer,
* ggf. die PMS-ID,
* die Gruppe des Schülers oder alle Gruppen des Lehrers und
* alle Wettbewerbsteilnahmen.
### Gruppe anzeigen
Suchen sie nach der Gruppe unter Angabe des Gruppencodes der Gruppe. Wählen Sie die entsprechende Gruppe. Alternativ ist eine Suche über den Gruppen-Administrator oder über Mitglieder der Gruppe möglich (s. o.).
Die folgenden Informationen zu Gruppen werden angezeigt:
* die Guppen-Id,
* der Gruppenname,
* der Gruppencode,
* der Marker,
* der Gruppen-Administrator und
* alle Mitglieder der Gruppe (inkl. Logincode und Jahrgangsstufe).
### Teilnahmen anzeigen
Rufen Sie den Nutzer auf, dessen Teilnahme Sie sich anzeigen lassen wollen. Wählen Sie den entsprechenden Wettbewerb aus der Liste der Teilnahmen des Nutzers aus.
Die folgenden Informationen zu Teilnahmen werden angezeigt:
* der Nutzer,
* der Wettbewerb,
* der Startzeitpunkt der Teilnahme und
* alle Einsendungen in dieser Teilnahme mit Zeitpunkt und erziehlten Punkten.
### Teilnahmen zurücksetzen (löschen)
Rufen Sie den entsprechenden Nutzer auf. Vergewissern Sie sich, dass es sich um den richtigen Nutzer handelt, eine gelöschte Teilnahme lässt sich nicht wiederherstellen. Wählen sie die entsprechende Teilnahme aus der Liste der Teilnahmen aus. Löschen Sie dann die Teilnahme indem Sie auf `Teilnahme löschen!` klicken.
......@@ -119,16 +193,76 @@ Rufen Sie den entsprechenden Nutzer auf. Vergewissern Sie sich, dass es sich um
Rufen Sie den entsprechenden Nutzer auf. Vergewissern Sie sich, dass es sich um den richtigen Nutzer handelt, ein gelöschter Nutzer lässt sich nicht wiederherstellen. Löschen Sie alle ggf. vorhandenen Teilnahmen des Nutzers (s. o.) und löschen Sie dann den Nutzer indem Sie auf `Benutzer löschen!` klicken.
### Gruppe löschen
Rufen Sie die entsprechende Gruppe auf. Vergewissern Sie sich, dass es sich um die richtigen Gruppe handelt, eine gelöschte Gruppe lässt sich nicht wiederherstellen. Löschen Sie alle ggf. vorhandenen Mitglieder der Gruppe (s. o.) und löschen Sie dann die Gruppe indem Sie auf `Gruppe löschen!` klicken.
Rufen Sie die entsprechende Gruppe auf. Vergewissern Sie sich, dass es sich um die richtige Gruppe handelt, eine gelöschte Gruppe lässt sich nicht wiederherstellen. Löschen Sie alle ggf. vorhandenen Mitglieder der Gruppe (s. o.) und löschen Sie dann die Gruppe indem Sie auf `Gruppe löschen!` klicken.
### Wettbewerbsergebnisse herunterladen
Gehen Sie auf http://jwinf.de/admin/contest/. (Alternativ die Administrationsseite aufrufen (s.o.) und ganz unten auf `Wettbewerbsübersicht` klicken.)
Gehen Sie auf http://jwinf.de/admin/contest/. (Alternativ die Administrationsseite aufrufen (s. o.) und ganz unten auf `Wettbewerbsübersicht` klicken.)
Mit klick auf den entsprechenden Wettbewerb werden die Ergebnisse des Wettbewerbs zusammengestellt und heruntergeladen.
Mit Klick auf den entsprechenden Wettbewerb werden die Ergebnisse des Wettbewerbs zusammengestellt und heruntergeladen.
Achtung: Die Zusammenstellung der Ergebnisse nimmt viel Rechenleistung in Anspruch. Dies sollte nicht während eines laufenden Wettbewerbs und auf keinen Fall in Stoßzeiten durchgeführt werden, da das sonst zu schweren Erreichbarkeitsproblemen bei den Teilnehmern führen kann.
### Teilnahmezahlen ansehen
Unter https://jwinf.de/admin/dbstatus können jederzeit die Teilnahmezahlen aller Wettbewerbe abgerufen werden.
Achtung: Die Zusammenstellung der Teilnahmezahlen nimmt etwas Rechenleistung in Anspruch. Diese Seite sollte nicht sehr oft hintereinander aufgerufen werden, da das sonst zu Erreichbarkeitsproblemen bei den Teilnehmern führen kann.
\ No newline at end of file
Unter https://jwinf.de/dbstatus können jederzeit die Teilnahmezahlen aller Wettbewerbe abgerufen werden.
Die Angaben werden nur höchstens einmal pro Minute aktualisiert.
## Troubleshooting
Wenn Fehler auftreten ist es einerseits wichtig, den Fehler so schnell wie möglich zu beheben, damit die Schüler und Lehrer ohne große Probleme am Wettbewerb teilnehmen können. Andererseits ist es aber auch wichtig, die Fehlerursache zu finden um den Fehler auch in Zukunft vermeiden zu können.
Einige Fehler, die mit schlechter Erreichbarkeit der Wettbewerbsplattform zu tun haben, lassen sich nur schwer begrenzen. Sie können am Browser des Nutzers, an der Internetverbindung oder an der Plattform selbst liegen.
Wenn der Fehler live auftritt, sollte die Seite https://jwinf.de/debug vom Benutzer aufgerufen werden. Die dort aufgeführten Zahlen und Werte erlauben es, Schlussfolgen über die Ursache schlechter Erreichbarkeit zu ziehen.
Beispiel:
```
Browser: Firefox 85
Session: existing (448374) alive
Activity: 0 ( N1615298564 - B1615298563 )
Login status: not logged in login
Session token: jwMExv1l9K reset create
Connectiviy:
Local: 122, 121, 147
Remote: 119, 120, 143
```
Mögliche Interpretation der Angaben sind:
- Browser:
- Browsername und Version
- Zur Teilnahmen sind mindestens notwendig:
- Firefox 46
- Chrome 50
- Safari 10
- Edge 15
- Die Mindestversionen können im Laufe der Zeit steigen.
- Session:
- "not existing": Person hat möglicherweise Cookies deaktiviert. Cookies sind für die Benutzung der Seite notwendig.
- "existing (…) alive": gut
- nicht alive: Session ist abgelaufen. Browserneustart oder neu einloggen sollte helfen.
- Activity:
- Zeit seit letztem Seitenaufruf in Sekunden
- Login status:
- Ist Nutzer eingeloggt oder nicht
- "logged in as" gibt an:
- Benutzername
- Voller Name (in Klammern)
- "[Lehrer]" falls Lehrerstatus gesetzt
- OAuth-ID gibt PMS-ID an, falls sich Benutzer über das PMS eingeloggt hat
- Session token:
- Im Cookie gespeicherter Wert (technisches Detail)
- Connectiviy:
- Gibt an wie gut die Verbindung zum Server ist. Niedrige Werte sind besser (schneller)
- Am besten ein paar mal neuladen, um zu sehen, wie die Zahlen schwanken.
- Gute Zahlen sind kleiner als 200, in manchen Fällen bis zu 400.
- Sind die Zahlen mehrfach größer als 500 ist das ein Problem, Teilnahme könnte schwierig werden.
- Ist "local" viel großer als "remote" liegt ein Problem mit dem Server vor. Bitte sofort einen Admin benachrichtigen.
- Ist die dritte Zahl viel großer als die erste, ist die Geschwindigkeit der Verbindung langsam.
- Ist das sowohl bei "local" als auch bei "remote" der Fall, deutet das auf ein Problem auf der Nutzerseite hin.
- Sind alle Zahlen gleichmäßig hoch, ist die Latenz der Verbindung langsam.
- Ist das sowohl bei "local" als auch bei "remote" der Fall, deutet das auf ein Problem auf der Nutzerseite hin.
# How to run Medal under Windows (64 bit)
Prerequisites: Rust, Cargo and git are already installed
## 1. Install vcpkg
Description by Microsoft: https://docs.microsoft.com/en-us/cpp/build/vcpkg?view=vs-2019
Basically:
1. Clone git repository from https://github.com/Microsoft/vcpkg
2. In new folder „vcpkg“ run „bootstrap-vcpkg.bat“
## 2. Install OpenSSL and sqlite3 with vcpkg
1. Go to the folder vcpkg via the commandline
2. Enter the following commands:
```
vcpkg install openssl:x64-windows
vcpkg install sqlite:x64-windows
```
## 3. Set the environment variables:
The following environment variables need to be set:
- OPENSSL_DIR should point to „vcpkg\packages\openssl-windows_x64-windows“ (This means in my case: `C:\Users\<Nutzername>\Documents\vcpkg\packages\openssl-windows_x64-windows`)
- OPENSSL_LIB_DIR should point to „vcpkg\packages\openssl-windows_x64-windows\lib“
- SQLITE3_LIB_DIR should point to „vcpkg\packages\sqlite3_x64-windows\lib“
## 4. Copy sqlite3.dll into the right place
Copy „sqlite3.dll“ from `\vcpkg\packages\sqlite3_x64-windows\bin` to `medal-prototype`.
# Medal unter Windows (64 bit) zum laufen bringen
Voraussetzung: Rust und Cargo sind bereits installiert
Voraussetzung: Rust und Cargo, sowie git sind bereits installiert
## 1. vcpkg installieren:
Ausführliche Anleitung von Microsoft: https://docs.microsoft.com/en-us/cpp/build/vcpkg?view=vs-2019
......
......@@ -51,11 +51,14 @@ pub struct Config {
pub allow_sex_other: Option<bool>,
pub dbstatus_secret: Option<String>,
pub template_params: Option<::std::collections::BTreeMap<String, serde_json::Value>>,
pub only_contest_scan: Option<bool>,
pub reset_admin_pw: Option<bool>,
}
#[derive(StructOpt, Debug)]
#[structopt()]
pub struct Opt {
struct Opt {
/// Config file to use (default: 'config.json')
#[structopt(short = "c", long = "config", default_value = "config.json", parse(from_os_str))]
pub configfile: PathBuf,
......@@ -72,6 +75,10 @@ pub struct Opt {
#[structopt(short = "p", long = "port")]
pub port: Option<u16>,
/// Teacher page in task directory
#[structopt(short = "t", long = "template")]
pub template: Option<String>,
/// Reset password of admin user (user_id=1)
#[structopt(short = "a", long = "reset-admin-pw")]
pub resetadminpw: bool,
......@@ -97,7 +104,7 @@ pub struct Opt {
pub enablepasswordlogin: bool,
/// Teacher page in task directory
#[structopt(short = "t", long = "teacherpage")]
#[structopt(short = "T", long = "teacherpage")]
pub teacherpage: Option<String>,
}
......@@ -148,3 +155,41 @@ pub fn read_config_from_file(file: &Path) -> Config {
config
}
fn merge_value<T>(into: &mut Option<T>, from: Option<T>) { from.map(|x| *into = Some(x)); }
fn merge_flag(into: &mut Option<bool>, from: bool) {
if from {
*into = Some(true);
}
}
pub fn get_config() -> Config {
let opt = Opt::from_args();
#[cfg(feature = "debug")]
println!("Options: {:#?}", opt);
let mut config = read_config_from_file(&opt.configfile);
#[cfg(feature = "debug")]
println!("Config: {:#?}", config);
// Let options override config values
merge_value(&mut config.database_file, opt.databasefile);
merge_value(&mut config.database_url, opt.databaseurl);
merge_value(&mut config.port, opt.port);
merge_value(&mut config.template, opt.template);
merge_flag(&mut config.no_contest_scan, opt.nocontestscan);
merge_flag(&mut config.open_browser, opt.openbrowser);
merge_flag(&mut config.disable_results_page, opt.disableresultspage);
merge_flag(&mut config.enable_password_login, opt.enablepasswordlogin);
merge_flag(&mut config.only_contest_scan, opt.onlycontestscan);
merge_flag(&mut config.reset_admin_pw, opt.resetadminpw);
// Use default database file if none set
config.database_file.get_or_insert(Path::new("medal.db").to_owned());
config
}
......@@ -15,6 +15,7 @@
use db_objects::{Contest, Task, Taskgroup};
use serde_yaml;
use std::path::Path;
extern crate time;
......@@ -40,8 +41,16 @@ struct ContestYaml {
// The task path is stored relatively to the contest.yaml for easier identificationy
// Concatenation happens in functions::show_task
pub fn parse_yaml(content: &str, filename: &str, directory: &str) -> Option<Contest> {
let config: ContestYaml = serde_yaml::from_str(&content).unwrap();
fn parse_yaml(content: &str, filename: &str, directory: &str) -> Option<Contest> {
let config: ContestYaml = match serde_yaml::from_str(&content) {
Ok(contest) => contest,
Err(e) => {
eprintln!();
eprintln!("{}", e);
eprintln!("Error loading contest YAML: {}{}", directory, filename);
panic!("Loading contest file")
}
};
use self::time::{strptime, Timespec};
......@@ -63,6 +72,7 @@ pub fn parse_yaml(content: &str, filename: &str, directory: &str) -> Option<Cont
config.max_grade,
config.position,
config.requires_login,
// Consumed by `let required_contests = contest.requires_contest.as_ref()?.split(',');` in core.rs
config.requires_contest.map(|list| list.join(",")),
config.secret,
config.message);
......@@ -118,3 +128,46 @@ pub fn parse_yaml(content: &str, filename: &str, directory: &str) -> Option<Cont
Some(contest)
}
fn read_contest(p: &Path) -> Option<Contest> {
use std::fs::File;
use std::io::Read;
let mut file = File::open(p).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).ok()?;
parse_yaml(&contents, p.file_name().to_owned()?.to_str()?, &format!("{}/", p.parent().unwrap().to_str()?))
}
pub fn get_all_contest_info(task_dir: &str) -> Vec<Contest> {
fn walk_me_recursively(p: &Path, contests: &mut Vec<Contest>) {
if let Ok(paths) = std::fs::read_dir(p) {
print!("…");
use std::io::Write;
std::io::stdout().flush().unwrap();
let mut paths: Vec<_> = paths.filter_map(|r| r.ok()).collect();
paths.sort_by_key(|dir| dir.path());
for path in paths {
let p = path.path();
walk_me_recursively(&p, contests);
}
}
if p.file_name().unwrap().to_string_lossy().to_string().ends_with(".yaml") {
read_contest(p).map(|contest| contests.push(contest));
};
}
let mut contests = Vec::new();
match std::fs::read_dir(task_dir) {
Err(why) => println!("Error opening tasks directory! {:?}", why.kind()),
Ok(paths) => {
for path in paths {
walk_me_recursively(&path.unwrap().path(), &mut contests);
}
}
};
contests
}
......@@ -14,6 +14,7 @@
use time;
use config::OauthProvider;
use db_conn::MedalConnection;
#[cfg(feature = "signup")]
use db_conn::SignupResult;
......@@ -21,7 +22,6 @@ use db_objects::OptionSession;
use db_objects::SessionUser;
use db_objects::{Contest, Grade, Group, Participation, Submission, Taskgroup};
use helpers;
use config::OauthProvider;
use webfw_iron::{json_val, to_json};
#[derive(Serialize, Deserialize)]
......@@ -121,8 +121,7 @@ fn grade_to_string(grade: i32) -> String {
}
}
pub fn index<T: MedalConnection>(conn: &T, session_token: Option<String>, login_info: LoginInfo)
-> MedalValueResult {
pub fn index<T: MedalConnection>(conn: &T, session_token: Option<String>, login_info: LoginInfo) -> MedalValueResult {
let mut data = json_val::Map::new();
if let Some(token) = session_token {
......@@ -158,7 +157,8 @@ pub fn show_login<T: MedalConnection>(conn: &T, session_token: Option<String>, l
("login".to_owned(), data)
}
pub fn status<T: MedalConnection>(conn: &T, config_secret: Option<String>, given_secret: Option<String>) -> MedalResult<String> {
pub fn status<T: MedalConnection>(conn: &T, config_secret: Option<String>, given_secret: Option<String>)
-> MedalResult<String> {
if config_secret == given_secret {
Ok(conn.get_debug_information())
} else {
......@@ -218,8 +218,7 @@ pub enum ContestVisibility {
pub fn show_contests<T: MedalConnection>(conn: &T, session_token: &str, login_info: LoginInfo,
visibility: ContestVisibility)
-> MedalValueResult
{
-> MedalValueResult {
let mut data = json_val::Map::new();
let session = conn.get_session_or_new(&session_token).map_err(|_| MedalError::DatabaseConnectionError)?;
......@@ -293,6 +292,7 @@ pub struct ContestStartConstraints {
}
fn check_contest_qualification<T: MedalConnection>(conn: &T, session: &SessionUser, contest: &Contest) -> Option<bool> {
// Produced by `config.requires_contest.map(|list| list.join(",")),` in contestreader_yaml.rs
let required_contests = contest.requires_contest.as_ref()?.split(',');
for req_contest in required_contests {
......@@ -323,14 +323,12 @@ fn check_contest_constraints(session: &SessionUser, contest: &Contest) -> Contes
contest_running,
grade_too_low,
grade_too_high,
grade_matching,
}
grade_matching }
}
pub fn show_contest<T: MedalConnection>(conn: &T, contest_id: i32, session_token: &str,
query_string: Option<String>, login_info: LoginInfo, secret: Option<String>)
-> MedalValueResult
{
-> MedalValueResult {
let session = conn.get_session_or_new(&session_token).unwrap();