aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cockpit/index.html8
-rw-r--r--cockpit/js/cockpit.js84
-rw-r--r--js/config.js7
-rw-r--r--js/database.js10
-rw-r--r--js/graduation-common.js161
-rw-r--r--js/mock.js6
-rw-r--r--js/student-common.js22
-rw-r--r--mass-graducation/index.css21
-rw-r--r--mass-graducation/index.html19
-rw-r--r--mass-graducation/package-lock.json4
-rw-r--r--mass-graducation/src/main.js163
11 files changed, 436 insertions, 69 deletions
diff --git a/cockpit/index.html b/cockpit/index.html
index dab2c89..cea2c60 100644
--- a/cockpit/index.html
+++ b/cockpit/index.html
@@ -123,6 +123,14 @@
<tbody id="graduation-current-observation-period">
</tbody>
</table>
+
+ <div id="graduation-current-observation-period-end" style="display:none;">
+ <textarea id="graduation-current-observation-period-end-reason" rows="5" spellcheck="default">
+ </textarea>
+ Aktuellen Beobachtungszeitraum abschließen:
+ <button id="graduation-current-observation-period-end-success">Erfolreich</button>
+ <button id="graduation-current-observation-period-end-failure">Nicht Erfolreich</button>
+ </div>
</details>
<details id="graduation-observation-periods-container" class="graduation-dropdown">
diff --git a/cockpit/js/cockpit.js b/cockpit/js/cockpit.js
index c0b20c2..15219cd 100644
--- a/cockpit/js/cockpit.js
+++ b/cockpit/js/cockpit.js
@@ -4,6 +4,7 @@
// } from "../../js/mock.js";
import {
+ OBSERVATION_TYPES,
LEVEL_NAMES,
GraduationChange,
fetchGraduation,
@@ -11,7 +12,7 @@ import {
submitGraduationChange
} from "../../js/graduation-ENV.js";
-import { fetchStudent, fetchStudents } from "../../js/student-ENV.js";
+import { fetchStudent, fetchStudents, Student } from "../../js/student-ENV.js";
import {
isTeacher as getIsTeacher,
getName,
@@ -32,11 +33,6 @@ async function loadStudent(studentToLoad) {
updateStudentGraduation();
}
-async function loadNewStudent(id) {
- student = await fetchStudent(id);
- await loadStudent(student);
-}
-
function initStudentSelection() {
initSelection(
"student-selection",
@@ -174,11 +170,13 @@ function renderObservationStatus(graduation, node) {
node.innerHTML = "";
let status;
- if (isTeacher || graduation.isObserved()) {
+ if (isTeacher || graduation.isObserved() || graduation.observationRequested) {
status = document.createElement("span");
status.classList.add("student-observation-status");
if (graduation.isObserved()) {
status.innerHTML = '<span style="font-size: larger;">&#128065</span> Ich befinde mich in der Beobachtungsphase zum Levelaufstieg.';
+ } else if (graduation.observationRequested) {
+ status.innerHTML = 'Beobachtungsphase noch nicht begonnen.';
} else {
status.innerHTML = "Nicht in Beobachtung.";
}
@@ -274,6 +272,69 @@ async function updateStudentGraduation() {
}
changesTbl.appendChild(changeTr);
}
+
+ const observtionsTbl = document.getElementById("graduation-current-observation-period");
+ observtionsTbl.innerHTML = "";
+ if (graduation.observation) {
+ const sums = graduation.observation.getObservationsSums();
+ for (let i = 0; i < OBSERVATION_TYPES.length; i++) {
+ const tr = document.createElement("tr");
+ const typeTd = document.createElement("td");
+ typeTd.innerHTML = OBSERVATION_TYPES[i];
+ tr.appendChild(typeTd);
+
+ const yesTd = document.createElement("td");
+ yesTd.innerHTML = sums[i].yes;
+ tr.appendChild(yesTd);
+
+ const noTd = document.createElement("td");
+ noTd.innerHTML = sums[i].no;
+ tr.appendChild(noTd);
+
+ observtionsTbl.appendChild(tr);
+ }
+
+ const endDiv = document.getElementById("graduation-current-observation-period-end");
+ if (!graduation.observation.ready()) {
+ endDiv.style.display = "none";
+ return;
+ }
+
+ endDiv.style.display = "block";
+
+ const reason = document.getElementById("graduation-current-observation-period-end-reason").value;
+ const successBtn = document.getElementById("graduation-current-observation-period-end-success");
+ successBtn.addEventListener("click", () => {
+ submitGraduationChange(graduation.student, GraduationChange.newObservationEnd(reason, true));
+ });
+ const failureBtn = document.getElementById("graduation-current-observation-period-end-failure");
+ failureBtn.addEventListener("click", () => {
+ submitGraduationChange(graduation.student, GraduationChange.newObservationEnd(reason, false));
+ });
+ }
+
+ const pastObservtionsTbl = document.getElementById("graduation-observation-periods");
+ pastObservtionsTbl.innerHTML = "";
+ if (graduation.pastObservations.length > 0) {
+ for (const period of graduation.pastObservations) {
+ const tr = document.createElement("tr");
+ const periodTd = document.createElement("td");
+ const startDate = new Intl.DateTimeFormat("de-DE", { month: "numeric", day: "numeric" }).format(period.start);
+ const endDate = new Intl.DateTimeFormat("de-DE", { month: "numeric", day: "numeric", year: "numeric" }).format(period.end);
+ periodTd.innerHTML = `${startDate} - ${endDate}`;
+ tr.appendChild(periodTd);
+
+ const successTd = document.createElement("td");
+ if (period.success) {
+ successTd.innerHTML = "Graduierung erfolgreich"
+ } else {
+ successTd.innerHTML = "Graduierung nicht erfolgreich"
+ }
+ tr.appendChild(successTd);
+
+ pastObservtionsTbl.appendChild(tr);
+ }
+ }
}
async function initCockpit() {
@@ -281,15 +342,18 @@ async function initCockpit() {
initStudentSelection();
const params = new URLSearchParams(location.search);
const userId = params.get("userToLoad");
- if (userId)
- loadNewStudent(userId);
+ if (userId) {
+ student = await fetchStudent(userId);
+ await loadStudent(student);
+ }
} else {
// Remove all elements only suitable for teachers
for(const elem of document.querySelectorAll("[data-availability=if-lehrer]")) {
elem.remove();
}
- loadNewStudent(getUserId());
+ const student = await Student.fetchById(getUserId());
+ await loadStudent(student);
}
}
diff --git a/js/config.js b/js/config.js
index f07c5ec..5fc456c 100644
--- a/js/config.js
+++ b/js/config.js
@@ -3,6 +3,12 @@ function getCourseId() {
return courseId;
}
+// TODO(muhq): retrieve when loading activities/DBs
+const cockpidId = 93017047;
+function getCockpitId() {
+ return cockpidId;
+}
+
function getCourseParticipantsUrl() {
return `https://lernplattform.bycs.de/user/index.php?id=${courseId}`;
}
@@ -17,6 +23,7 @@ async function getUserIconUrl(userId) {
export {
getCourseId,
+ getCockpitId,
getUserIconUrl,
getCourseParticipantsUrl
};
diff --git a/js/database.js b/js/database.js
index 7fd3d47..73c2d17 100644
--- a/js/database.js
+++ b/js/database.js
@@ -29,7 +29,7 @@ async function populateStudentDbs() {
const courseDoc = new DOMParser().parseFromString(await courseRes.text(), 'text/html');
let dbs = new Map();
- for (const link of courseDoc.querySelectorAll("div.activityname > a.aalink")) {
+ for (const link of courseDoc.querySelectorAll("div.activityname a.aalink")) {
const match = link.textContent.match(/(\d+)(Shadow|Public|Submit)/);
if (!match) continue;
const studentId = match[1];
@@ -82,7 +82,13 @@ async function loadFromDatabaseActivity(id, params) {
let entry = {};
row.querySelectorAll('.db-cell').forEach(field => {
const key = field.dataset.key;
- const value = field.innerText.trim();
+ let value;
+ // Moodle includes the full date in the title attribute of the included span
+ if (key === "date") {
+ value = field.querySelector("span").title;
+ } else {
+ value = field.innerText.trim();
+ }
entry[key] = value;
});
data.push(entry);
diff --git a/js/graduation-common.js b/js/graduation-common.js
index 3ab1fdd..1c68f38 100644
--- a/js/graduation-common.js
+++ b/js/graduation-common.js
@@ -22,11 +22,15 @@ class GraduationChange {
publicDBFields () {
return {
"change": this.change,
+ "date": this.date,
};
}
dbFields () {
return {
+ // We include the date in the json because Moodle reports the timeadded
+ // locale dependent.
+ "date": this.date,
"change": this.change,
"reason": this.reason,
"context": this.context,
@@ -34,9 +38,25 @@ class GraduationChange {
}
static fromDBEntry(e) {
- const {date, coach, json} = e;
+ const {coach, json} = e;
const change = JSON.parse(json);
- return new GraduationChange(new Date(date), change.change, change.reason, coach, change.context);
+ return new GraduationChange(new Date(change.date), change.change, change.reason, coach, change.context);
+ }
+
+ static newObservationRequest() {
+ return new GraduationChange(new Date(), "ObservationRequest", "", "", "");
+ }
+
+ static newObservationStart() {
+ return new GraduationChange(new Date(), "ObservationStart", "", "", "");
+ }
+
+ static newObservationEnd(reason, success) {
+ return new GraduationChange(new Date(), "ObservationEnd", reason, "", JSON.stringify(success));
+ }
+
+ static newObservationChange(observation) {
+ return new GraduationChange(observation.date, "Observation", "", "", JSON.stringify(observation.status));
}
static newGreenPointChange(reason, coach) {
@@ -53,14 +73,16 @@ class GraduationChange {
}
class Graduation {
- constructor(student, green, orange, red, level, stickers, observation, changes) {
+ constructor(student, green, orange, red, level, stickers, observationRequested, observation, pastObservations, changes) {
this.student = student;
this.green = green;
this.orange = orange;
this.red = red;
this.level = level;
this.stickers = stickers;
+ this.observationRequested = observationRequested;
this.observation = observation;
+ this.pastObservations = pastObservations;
this.changes = changes;
student.graduation = this;
@@ -71,41 +93,66 @@ class Graduation {
let orange = 0;
let red = 0;
let level = 1;
+ let openObservationRequest = null;
let observationPeriod = null;
let pastObservations = [];
let stickers = [];
+
+ // The parsing logic assumes that changes are ordered chronologically
+ changes.sort((a, b) => a.date - b.date);
+
for (const change of changes) {
switch (change.change) {
case "Grün":
green++;
break;
+
case "Orange":
orange++;
break;
+
case "Rot":
red++;
break;
+
+ case "ObservationRequest":
+ openObservationRequest = change.date;
+ break;
+
case "ObservationStart":
- observationPeriod = new ObservationPeriod(change.date, null, []);
+ console.assert(openObservationRequest, "No request for observation start");
+ observationPeriod = new ObservationPeriod(change.date);
+ openObservationRequest = null;
break;
+
case "ObservationEnd":
- observationPeriod.end = change.date;
+ console.assert(observationPeriod, "No active observation period");
+ observationPeriod.finishPeriod(change.context.success);
+ if (change.context.success) {
+ level++;
+ }
pastObservations.push(observationPeriod);
observationPeriod = null;
break;
+
case "Observation":
- const observation = Observation.fromContext(change.context);
- if (observationPeriod) {
- observationPeriod.addObservation(observation);
- } else {
- alert(`No active observation period`);
- }
+ console.assert(observationPeriod, "No active observation period");
+ const observation = Observation.fromChange(change);
+ observationPeriod.addObservation(observation);
+ break;
+
+ case "LevelChange":
+ level = change.context.level;
break;
+
default:
alert(`Unexpected change: ${change}`);
}
}
- return new Graduation(student, green, orange, red, level, stickers, observationPeriod, changes);
+ return new Graduation(student, green, orange, red, level,
+ stickers,
+ openObservationRequest, observationPeriod, pastObservations,
+ changes);
}
addChange(change) {
@@ -120,7 +167,36 @@ class Graduation {
* Request observation
*/
async requestObservation() {
- await addEntryToStudentDatabase(this.student, "submit", {change: "ObservationRequest"});
+ const request = GraduationChange.newObservationRequest();
+ this.observationRequested = request.date;
+ await addEntryToStudentDatabase(this.student, "submit", JSON.stringify(request.publicDBFields()));
+ }
+
+ /**
+ * Start an observation
+ */
+ async startObservation() {
+ if (!this.observationRequested) {
+ console.assert(this.observationRequested, "No observation requested");
+ return;
+ }
+ this.observationRequested = null;
+ this.observation = new ObservationPeriod(new Date());
+ return submitGraduationChange(this.student, GraduationChange.newObservationStart());
+ }
+
+ /**
+ * End an observation
+ */
+ async endObservation(reason, success) {
+ if (!this.observation) {
+ console.assert(this.observation, "No active observation");
+ return;
+ }
+ this.observation.end = new Date();
+ this.pastObservations.push(this.observation);
+ this.observation = null;
+ return submitGraduationChange(this.student, GraduationChange.newObservationEnd(reason, success));
}
/**
@@ -147,10 +223,14 @@ class Graduation {
}
class ObservationPeriod {
- constructor(start, end, observations) {
+ constructor(start) {
this.start = start;
- this.end = end;
- this.observations = observations;
+ this.observations = [];
+ }
+
+ finishPeriod(success) {
+ this.end = new Date();
+ this.success = success;
}
ended() {
@@ -158,18 +238,28 @@ class ObservationPeriod {
}
ready() {
- // TODO(muhq): add time contraint
- return this.observations > 10;
+ if (this.observations.length < 10)
+ return false;
+
+ // at least 10 observation on different days
+ let days = new Map();
+ for (const observation of this.observations) {
+ const d = observation.date;
+ days.set(`${d.getFullYear()}${d.getMonth()}${d.getDate()}`, true);
+ }
+ return days.size >= 10;
}
getObservationsSums() {
- let sums = Array(OBSERVATION_TYPES.length).fill({"yes":0, "no":0});
+ let sums = Array(OBSERVATION_TYPES.length).fill().map(() => ({"yes":0, "no":0}));
for (const observation of this.observations) {
for (let i = 0; i < sums.length; i++) {
- if (observation.status[i])
- sums[i].yes++;
- else
- sums[i].no++;
+ if (observation.status[i]) {
+ if (observation.status[i] === "yes")
+ sums[i].yes++;
+ else
+ sums[i].no++;
+ }
}
}
return sums;
@@ -192,10 +282,14 @@ const OBSERVATION_TYPES = [
class Observation {
constructor(date, status) {
- console.assert(status.length != OBSERVATION_TYPES.length, "invalid observation status");
+ console.assert(status.length == OBSERVATION_TYPES.length, "invalid observation status");
this.date = date;
this.status = status;
}
+
+ static fromChange(change) {
+ return new Observation(change.date, JSON.parse(change.context))
+ }
}
async function fetchGraduationChangesFromDb(student, type) {
@@ -206,15 +300,23 @@ async function fetchGraduationChangesFromDb(student, type) {
const gradChange = GraduationChange.fromDBEntry(e);
changes.push(gradChange);
}
- return changes;
+ return changes.flat();
}
async function fetchGraduationChanges(student) {
- return fetchGraduationChangesFromDb(student, "shadow");
+ let changes = await Promise.all([
+ fetchGraduationChangesFromDb(student, "shadow"),
+ fetchGraduationChangesFromDb(student, "submit"),
+ ]);
+ return changes.flat();
}
async function fetchPublicGraduationChanges(student) {
- return fetchGraduationChangesFromDb(student, "public");
+ let changes = await Promise.all([
+ fetchGraduationChangesFromDb(student, "public"),
+ fetchGraduationChangesFromDb(student, "submit"),
+ ]);
+ return changes.flat();
}
async function fetchGraduation(student) {
@@ -248,8 +350,9 @@ async function submitGraduationChange(student, change) {
}
async function submitObservation(student, observation) {
- student.graduation.addObservation(observation);
- await addEntryToStudentDatabase(student, "shadow", JSON.stringify(observation));
+ console.assert(student.graduation.observation, "no active observation");
+ student.graduation.observation.addObservation(observation);
+ await submitGraduationChange(student, GraduationChange.newObservationChange(observation));
}
export {
diff --git a/js/mock.js b/js/mock.js
index 4bcd202..91b1525 100644
--- a/js/mock.js
+++ b/js/mock.js
@@ -83,13 +83,13 @@ function fetchPublicGraduationMock(s) {
}
function submitGraduationChangeMock(s, c) {
- assert(s.graduation, "no graduation")
+ console.assert(s.graduation, "no graduation")
s.graduation.addChange(c);
}
function submitObservationMock(s, o) {
- assert(s.graduation, "no graduation")
- assert(s.graduation.observation, "no observation period")
+ console.assert(s.graduation, "no graduation")
+ console.assert(s.graduation.observation, "no observation period")
s.graduation.observation.addObservation(o);
}
diff --git a/js/student-common.js b/js/student-common.js
index c89b818..778ff8d 100644
--- a/js/student-common.js
+++ b/js/student-common.js
@@ -1,4 +1,4 @@
-import { getUserIconUrl, getCourseParticipantsUrl } from "./config.js";
+import { getCourseId, getUserIconUrl, getCourseParticipantsUrl } from "./config.js";
import { fetchGraduation } from "./graduation-common.js";
/**
* Class to represent Students.
@@ -27,6 +27,26 @@ class Student {
}
return this.graduation;
}
+
+ static async fetchById(id) {
+ let name = "";
+ let group = "";
+
+ const res = await fetch(`/user/view.php?id=${id}&course=${getCourseId()}`);
+ const doc = new DOMParser().parseFromString(await res.text(), "text/html");
+
+ name = doc.querySelector("div.page-header-headings > h2").textContent;
+
+ const cards = doc.querySelectorAll("div.card-body dl");
+ for (const card of cards) {
+ if (card.querySelector("dt").textContent !== "Gruppe")
+ continue;
+ group = card.querySelector("dd > a").textContent;
+ break;
+ }
+
+ return new Student(id, name, group);
+ }
}
async function fetchStudent(idFilter, nameFilter, groupFilter) {
diff --git a/mass-graducation/index.css b/mass-graducation/index.css
index 28f4b88..a8790b8 100644
--- a/mass-graducation/index.css
+++ b/mass-graducation/index.css
@@ -72,6 +72,8 @@
}
.group-member {
+ display: grid;
+ grid-template-columns: 30px 30px 200px repeat(3, 40px) auto 20px;
}
.group-member-name {
@@ -88,12 +90,15 @@
.group-member-green {
background: green;
+ margin: 1px;
}
.group-member-orange {
background: orange;
+ margin: 1px;
}
.group-member-red {
background: red;
+ margin: 1px;
}
#group-member-point-reason {
@@ -115,4 +120,20 @@
.group-member-observation-container {
background: lightgray;
+ width: 600px;
}
+
+.group-member-observation-table {
+ background: lightgray;
+
+}
+.group-member-observation-table-description {
+ width: 80%;
+}
+
+.group-member-observation-table-yes,
+.group-member-observation-table-no {
+ width: 10%;
+ text-align: center;
+}
+
diff --git a/mass-graducation/index.html b/mass-graducation/index.html
index 1ed7042..7a65745 100644
--- a/mass-graducation/index.html
+++ b/mass-graducation/index.html
@@ -16,7 +16,7 @@
class="group-selection"
placeholder="Klasse"
/>
- <button id="group-selection-clear" display="style: none;">&times;</button>
+ <button id="group-selection-clear" style="display: none;">&times;</button>
</div>
<ul id="group-selection-list">
</ul>
@@ -26,9 +26,20 @@
</div>
<div id="group-content" style="display: none">
- <ul id="group-members">
- </ul>
+ <div id="group-member-container">
+ <ul id="group-members">
+ </ul>
+ </div>
+ <button id="submit-mass-graduation" style="display: none;">Speichern</button>
+ <span id="submit-mass-graduation-state" style="display: none;"></span>
+
+ <div id="observation-requests-container" style="display: none;">
+ <h3>Aktuelle Graduierungswünsche</h3>
+ <table>
+ <tbody id="observation-requests">
+ </tbody>
+ </table>
+ </div>
</div>
- <button id="submit-mass-graduation" style="display: none">Speichern</button>
</body>
</html>
diff --git a/mass-graducation/package-lock.json b/mass-graducation/package-lock.json
index 6ba8178..18aa850 100644
--- a/mass-graducation/package-lock.json
+++ b/mass-graducation/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "cockpit",
+ "name": "mass-graduation",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "cockpit",
+ "name": "mass-graduation",
"version": "1.0.0",
"devDependencies": {
"html-webpack-plugin": "^5.6.7",
diff --git a/mass-graducation/src/main.js b/mass-graducation/src/main.js
index bae20bb..3d20f0b 100644
--- a/mass-graducation/src/main.js
+++ b/mass-graducation/src/main.js
@@ -13,13 +13,16 @@ import { getName } from "../../js/utils-ENV.js";
let group;
let name;
-function toggle(btn) {
- if (btn.innerHTML === "X")
+function toggle(btn, other) {
+ if (btn.innerHTML === "X") {
btn.innerHTML = "o";
- else
+ } else {
btn.innerHTML = "X";
+ if (other)
+ other.innerHTML = "o";
+ }
- document.getElementById("submit-mass-graduation").style.display = "block";
+ document.getElementById("submit-mass-graduation").style.display = "inline";
}
function observationClickHandler(ob, li) {
@@ -36,11 +39,17 @@ function observationClickHandler(ob, li) {
li.appendChild(observationDiv);
const obsTbl = document.createElement("table");
- obsTbl.id = "group-member-observation-table";
+ obsTbl.classList.add("group-member-observation-table");
observationDiv.appendChild(obsTbl);
const obsTh = document.createElement("thead");
- obsTh.innerHTML = "<tr><td></td><td>Ja</td><td>Nein</td></tr>";
+ obsTh.innerHTML = `
+<tr>
+ <th></th>
+ <th class="group-member-observation-table-yes">Ja</th>
+ <th class="group-member-observation-table-no">Nein</th>
+</tr>`;
+
obsTbl.appendChild(obsTh);
const obsTbdy = document.createElement("tbody");
@@ -51,19 +60,28 @@ function observationClickHandler(ob, li) {
const obsDesc = document.createElement("td");
obsDesc.innerHTML = obsType;
+ obsDesc.classList.add("group-member-observation-table-description");
obsTr.appendChild(obsDesc);
const obsYes = document.createElement("td");
obsYes.innerHTML = "o";
- obsYes.addEventListener("click", () => toggle(obsYes));
obsYes.dataset.yes = "true";
+ obsYes.classList.add("group-member-observation-table-yes");
obsTr.appendChild(obsYes);
const obsNo = document.createElement("td");
obsNo.innerHTML = "o";
- obsNo.addEventListener("click", () => toggle(obsNo) );
+ obsNo.dataset.no = "true";
+ obsNo.classList.add("group-member-observation-table-no");
obsTr.appendChild(obsNo);
+ obsYes.addEventListener("click", () => {
+ toggle(obsYes, obsNo);
+ });
+ obsNo.addEventListener("click", () => {
+ toggle(obsNo, obsYes);
+ });
+
obsTbdy.appendChild(obsTr);
}
// Toggle the observation container visibility
@@ -79,6 +97,7 @@ function observationClickHandler(ob, li) {
async function loadGroup(selected) {
group = selected;
const students = await group.getStudents();
+ let graduationPromises = [];
document.getElementById("group-content").style.display = "block";
const ul = document.getElementById("group-members");
@@ -93,18 +112,22 @@ async function loadGroup(selected) {
const ob = document.createElement("button");
ob.classList.add("group-member-observation-icon")
- student.getGraduation().then((graduation) => {
+
+ const graduationPromise = student.getGraduation();
+ graduationPromise.then((graduation) => {
if (!graduation.isObserved()) {
return;
}
ob.innerHTML = "&#128065"
ob.addEventListener("click", () => observationClickHandler(ob, li) );
});
+ graduationPromises.push(graduationPromise);
+
li.appendChild(ob);
const i = document.createElement("img");
i.classList.add("group-member-icon")
- student.getGraduation().then((graduation) => {
+ graduationPromise.then((graduation) => {
i.src = graduation.getIcon();
});
li.appendChild(i);
@@ -134,42 +157,146 @@ async function loadGroup(selected) {
ul.appendChild(li);
}
+
+ const graduations = await Promise.all(graduationPromises);
+ let observations = graduations.filter(
+ (g) => g.observationRequested || (g.observation && !g.observation.end)
+ );
+
+ const observationReuqestsContainer = document.getElementById("observation-requests-container");
+
+ if (observations.length == 0) {
+ observationReuqestsContainer.style.display = "none";
+ return;
+ } else {
+ observationReuqestsContainer.style.display = "unset";
+ }
+
+ const observationReuqestsNode = document.getElementById("observation-requests");
+ observationReuqestsNode.innerHTML = "";
+ observations.sort((g1, g2) => {
+ let g1D, g2D;
+ g1D = g1.observationRequested ? g1.observationRequested : g1.observation.start;
+ g2D = g2.observationRequested ? g2.observationRequested : g2.observation.start;
+ return g1D - g2D;
+ });
+
+ for (const graduation of observations) {
+ const row = document.createElement("tr");
+ const iconCell = document.createElement("td");
+ const icon = document.createElement("img");
+ icon.src = graduation.getIcon();
+ row.appendChild(iconCell);
+
+ const nameCell = document.createElement("td");
+ nameCell.innerHTML = `${graduation.student.name}`;
+ row.appendChild(nameCell);
+
+ const dateCell = document.createElement("td");
+ let date;
+ if (graduation.observationRequested) {
+ date = graduation.observationRequested;
+ } else {
+ date = graduation.observation.start;
+ }
+ dateCell.innerHTML = date.toLocaleString("de-DE", { month: "numeric", day: "numeric", year: "numeric", hour: "numeric", minute: "numeric" });
+ row.appendChild(dateCell);
+
+ const btnCell = document.createElement("td");
+ if (graduation.observation && !graduation.observation.ready()) {
+ btnCell.innerHTML = "In Beobachtungsphase";
+ } else {
+ const startBtn = document.createElement("button");
+ if (graduation.observationRequested) {
+ startBtn.addEventListener("click", () => {
+ graduation.startObservation();
+ //TODO(muhq): Add observation icon-button.
+ startBtn.remove();
+ btnCell.innerHTML = "In Beobachtungsphase";
+ });
+ startBtn.innerHTML = `Beobachtungsphase beginnen`;
+ startBtn.classList.add("start-observation");
+ } else {
+ startBtn.addEventListener("click", () => {
+ window.open(`/mod/page/view.php?id=93017047&userToLoad=${graduation.student.id}`, '_blank');
+ });
+ startBtn.innerHTML = `Graduierung abschließen`;
+ startBtn.classList.add("end-observation");
+ }
+ btnCell.appendChild(startBtn);
+ }
+ row.appendChild(btnCell);
+
+ observationReuqestsNode.appendChild(row);
+ }
}
function submitMassGraduation() {
+ let promises = [];
for(const li of document.querySelectorAll("li.group-member")) {
const reason = li.querySelector(".group-member-point-reason").value;
const student = li.student;
if (li.querySelector(".group-member-green").innerHTML === "X") {
- submitGraduationChange(student, GraduationChange.newGreenPointChange(reason, name));
+ promises.push(submitGraduationChange(student, GraduationChange.newGreenPointChange(reason, name)));
}
if (li.querySelector(".group-member-orange").innerHTML === "X") {
- submitGraduationChange(student, GraduationChange.newOrangePointChange(reason, name));
+ promises.push(submitGraduationChange(student, GraduationChange.newOrangePointChange(reason, name)));
}
if (li.querySelector(".group-member-red").innerHTML === "X") {
- submitGraduationChange(student, GraduationChange.newRedPointChange(reason, name));
+ promises.push(submitGraduationChange(student, GraduationChange.newRedPointChange(reason, name)));
}
if (li.observationDiv) {
let status = [];
for (const row of li.observationDiv.querySelectorAll("tbody > tr")) {
- status.push(row.querySelector('td[data-yes="true"]').innerHTML === "X");
+ if (row.querySelector('td[data-yes="true"]').innerHTML === "X")
+ status.push("yes");
+ else if (row.querySelector('td[data-no="true"]').innerHTML === "X")
+ status.push("no");
+ else
+ status.push("");
}
- submitObservation(student, new Observation(Date.now(), status));
+ promises.push(submitObservation(student, new Observation(new Date(), status)));
}
}
+
+ const state = document.getElementById("submit-mass-graduation-state");
+ Promise.all(promises).then(() => {
+ state.style.display = "inline";
+ state.innerHTML = "✓";
+ state.style.color = "green";
+ for (const li of document.querySelectorAll("li.group-member")) {
+ if (!li.observationDiv)
+ continue;
+ li.observationDiv.remove();
+ li.observationDiv = null;
+ }
+ }).catch((error) => {
+ state.style.display = "inline";
+ state.innerHTML = "X";
+ state.style.color = "red";
+ console.log(error);
+ });
}
function printRedPointNote(li) {
print();
}
-window.onload = function() {
+async function init() {
+ document.getElementById("submit-mass-graduation").addEventListener("click", submitMassGraduation);
+ name = getName();
+
initSelection(
"group-selection",
async function() { return (await fetchGroups()).values(); },
(group) => `${group.name}`,
loadGroup,
);
- document.getElementById("submit-mass-graduation").addEventListener("click", submitMassGraduation);
- name = getName();
}
+
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", init);
+} else {
+ init();
+}
+