Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1d6a2fa
Changed version to v1.0.1
Schlaumeier5 Aug 17, 2025
7964f31
Merge branch 'v1.0.1' into temp
Schlaumeier5 Aug 21, 2025
6c590e1
Merge pull request #125 from Schlaumeier5/temp
Schlaumeier5 Aug 21, 2025
59ba770
Merge pull request #128 from Schlaumeier5/main
Schlaumeier5 Aug 21, 2025
c5b1f5f
Merge pull request #129 from Schlaumeier5/v1.0.1
Schlaumeier5 Aug 21, 2025
1ab7694
Moved session related stuff in webserver to subpackage "sessions"
Schlaumeier5 Aug 21, 2025
c5ffcfe
Moved HttpHeader into requests
Schlaumeier5 Aug 21, 2025
64c83c4
Moved handlers into handler subpackage
Schlaumeier5 Aug 21, 2025
eb538a9
Merge pull request #130 from Schlaumeier5/126-refactor-web-server-pac…
Schlaumeier5 Aug 22, 2025
0d77db6
Merge branch 'main' of https://github.com/Schlaumeier5/student-databa…
Schlaumeier5 Sep 22, 2025
29d6812
Added JLine as library for terminal operations
Schlaumeier5 Sep 23, 2025
2de6494
Command line is now initialized before server
Schlaumeier5 Sep 27, 2025
c8931c0
Upgraded to jline 3.30.6 to have better windows support
Schlaumeier5 Sep 27, 2025
2065632
Added command descriptions
Schlaumeier5 Sep 27, 2025
3c9f52f
Improved help command
Schlaumeier5 Oct 19, 2025
1e25062
Merge pull request #143 from Schlaumeier5/136-improve-command-line-in…
Schlaumeier5 Oct 19, 2025
715f0a8
Merge branch 'v1.0.1' into temp
Schlaumeier5 Nov 4, 2025
7d8ea8e
Merge pull request #145 from Schlaumeier5/temp
Schlaumeier5 Nov 4, 2025
6d34b96
Update version to v1.0.1-SNAPSHOT-1
Schlaumeier5 Nov 4, 2025
3c475b1
Added modules.html
Schlaumeier5 Nov 9, 2025
cc27484
Added Modules
Schlaumeier5 Nov 9, 2025
d912857
Added module requests
Schlaumeier5 Nov 9, 2025
1338c45
Changed result view to use the settings
Schlaumeier5 Nov 9, 2025
7299e7b
Merge branch 'v1.0.1' of https://github.com/Schlaumeier5/student-data…
Schlaumeier5 Nov 9, 2025
fd96804
Merge pull request #152 from Learn-Monitor/150-add-modules
Schlaumeier5 Nov 9, 2025
06c0a2e
Merge pull request #151 from Learn-Monitor/149-add-module-settings-page
Schlaumeier5 Nov 9, 2025
9936699
Refactored SQLHelper
Schlaumeier5 Nov 18, 2025
f67cbd1
Merge pull request #163 from Learn-Monitor/102-remove-redundant-code-…
Schlaumeier5 Nov 18, 2025
94f5440
Added student-database.js
Schlaumeier5 Dec 13, 2025
2be2fea
Added templates
Schlaumeier5 Dec 13, 2025
dc530f6
Removing repetitive html code for teachers
Schlaumeier5 Dec 13, 2025
077086d
Removed repetitive html code for students
Schlaumeier5 Dec 13, 2025
a8de77f
Removed repetitive teacher js code
Schlaumeier5 Dec 13, 2025
b49a10b
Removed repetitive js code for students
Schlaumeier5 Dec 13, 2025
38c06c8
feat: Implement WebPath and GetRequestHandler for dynamic request han…
Schlaumeier5 Dec 13, 2025
a011615
fixed many bugs due to new templating feature
Schlaumeier5 Jan 7, 2026
20e0f63
Merge pull request #164 from Learn-Monitor/105-make-repetitive-html-c…
Schlaumeier5 Jan 7, 2026
1fc0f28
Added new CSV downloads:
Schlaumeier5 Jan 7, 2026
2f8ffc5
Merge pull request #165 from Learn-Monitor/148-add-csv-downloads
Schlaumeier5 Jan 7, 2026
5cb4c41
Fixed a few tests that failed
Schlaumeier5 Jan 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ plugins {
}

group = "igs-landstuhl"
version = "v1.0-PATCH-3"

version = "v1.0.1-SNAPSHOT-1"

application {
mainClass.set("de.igslandstuhl.database.Application")
}
Expand All @@ -19,8 +21,10 @@ dependencies {
implementation("com.google.code.gson:gson:2.13.1")
implementation("commons-codec:commons-codec:1.19.0")
implementation("com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1")
implementation("org.jline:jline:3.30.6") // for better console input handling

testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") // using JUnit 5 (latest)
testImplementation("org.junit.jupiter:junit-jupiter:5.13.4") // using JUnit 5 (latest)
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.test {
Expand Down
19 changes: 14 additions & 5 deletions src/main/java/de/igslandstuhl/database/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
import de.igslandstuhl.database.api.SerializationException;
import de.igslandstuhl.database.api.Subject;
import de.igslandstuhl.database.api.Topic;
import de.igslandstuhl.database.api.modules.WebModule;
import de.igslandstuhl.database.holidays.Holiday;
import de.igslandstuhl.database.server.Server;
import de.igslandstuhl.database.server.commands.Command;
import de.igslandstuhl.database.server.webserver.PostRequestHandler;
import de.igslandstuhl.database.server.webserver.WebPath;
import de.igslandstuhl.database.server.webserver.handlers.GetRequestHandler;
import de.igslandstuhl.database.server.webserver.handlers.PostRequestHandler;
import de.igslandstuhl.database.utils.CommandLineUtils;

/**
Expand Down Expand Up @@ -92,18 +95,24 @@ public Topic[] readFile(String file) throws SerializationException, SQLException

public static void main(String[] args) throws Exception {
instance = new Application(args);

if (!getInstance().suppressCmd()) {
Command.registerCommands();
CommandLineUtils.setup();
}

Server.getInstance().getConnection().createTables();

Holiday.setupCurrentSchoolYear();
PostRequestHandler.registerHandlers();
WebModule.registerModules();

WebPath.registerPaths();
GetRequestHandler.getInstance().registerHandlers();

if (getInstance().runsWebServer()) {
Server.getInstance().getWebServer().start();
}
if (!getInstance().suppressCmd()) {
Command.registerCommands();
CommandLineUtils.setup();
}

while (true) {
if (!getInstance().suppressCmd()) {
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/de/igslandstuhl/database/Registry.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@
import java.util.Map;
import java.util.stream.Stream;

import de.igslandstuhl.database.api.modules.WebModule;
import de.igslandstuhl.database.server.commands.Command;
import de.igslandstuhl.database.server.commands.CommandDescription;
import de.igslandstuhl.database.server.webserver.WebPath;
import de.igslandstuhl.database.server.webserver.handlers.HttpHandler;
import de.igslandstuhl.database.server.webserver.requests.APIPostRequest;
import de.igslandstuhl.database.server.webserver.requests.GetRequest;
import de.igslandstuhl.database.server.webserver.requests.HttpHandler;

public class Registry<K, V> implements Closeable {
private static final Registry<String,Command> COMMAND_REGISTRY = new Registry<>();
private static final Registry<String,CommandDescription> COMMAND_DESCRIPTION_REGISTRY = new Registry<>();
private static final Registry<String,HttpHandler<APIPostRequest>> POST_HANDLER_REGISTRY = new Registry<>();
private static final Registry<String,HttpHandler<GetRequest>> GET_HANDLER_REGISTRY = new Registry<>();
private static final Registry<String,WebModule> MODULE_REGISTRY = new Registry<>();
private static final Registry<String,WebPath> WEB_PATH_REGISTRY = new Registry<>();
public static Registry<String,Command> commandRegistry() {
return COMMAND_REGISTRY;
}
Expand All @@ -23,6 +29,15 @@ public static Registry<String, HttpHandler<APIPostRequest>> postRequestHandlerRe
public static Registry<String, HttpHandler<GetRequest>> getRequestHandlerRegistry() {
return GET_HANDLER_REGISTRY;
}
public static Registry<String, WebModule> moduleRegistry() {
return MODULE_REGISTRY;
}
public static Registry<String, CommandDescription> commandDescriptionRegistry() {
return COMMAND_DESCRIPTION_REGISTRY;
}
public static Registry<String, WebPath> webPathRegistry() {
return WEB_PATH_REGISTRY;
}

private final Map<K,V> objects = new HashMap<>();

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/de/igslandstuhl/database/api/Room.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ public static Room getRoom(String label) {
} else {
try {
Room room = Server.getInstance().processSingleRequest(Room::fromSQLFields, "get_room_by_label", SQL_FIELDS, label);
if (room == null) {
return null;
}
rooms.put(label, room);
return room;
} catch (SQLException e) {
Expand Down
79 changes: 78 additions & 1 deletion src/main/java/de/igslandstuhl/database/api/SchoolClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -326,5 +328,80 @@ public boolean equals(Object obj) {
public String toJSON() {
return "{\"id\": " + id + ", \"label\": \"" + label + "\", \"grade\": " + grade + "}";
}


/**
* Generates a CSV representation of completed tasks for each student in the class for a given subject.
* @param subject the subject for which to generate the CSV
* @return a CSV string representing the completed tasks of each student for this subject
*/
public String getCompletedTasksCSV(Subject subject) {
StringBuilder csvBuilder = new StringBuilder();
List<Student> students = getStudents();
List<Task> tasks = new LinkedList<>();
subject.getTopics(grade).forEach((t) -> tasks.addAll(t.getTasks()));
csvBuilder.append("Student ID,First Name,Last Name,Email");
tasks.forEach((t) -> csvBuilder.append(",").append(t.getNumber()));
csvBuilder.append("\n");
for (Student student : students) {
csvBuilder.append(student.getId()).append(",")
.append(student.getFirstName()).append(",")
.append(student.getLastName()).append(",")
.append(student.getEmail());
for (Task task : tasks) {
boolean completed = student.hasCompletedTask(task);
csvBuilder.append(",").append(completed ? "1" : "0");
}
csvBuilder.append("\n");
}
return csvBuilder.toString();
}

/**
* Generates a CSV representation of the results for each student in the class for a given subject.
* @param subject the subject for which to generate the CSV
* @return a CSV string representing the results of each student for this subject
*/
public String getResultsCSV(Subject subject) {
StringBuilder csvBuilder = new StringBuilder();
List<Student> students = getStudents();

csvBuilder.append("Student ID,First Name,Last Name,Email,Total Score(%)\n");
for (Student student : students) {
double totalScore = student.getCurrentProgress(subject) * 100.;
csvBuilder.append(student.getId()).append(",")
.append(student.getFirstName()).append(",")
.append(student.getLastName()).append(",")
.append(student.getEmail()).append(",")
.append(String.format(Locale.US, "%.2f", totalScore)).append("\n");
}

return csvBuilder.toString();
}
/**
* Generates a CSV representation of the results for all students in all classes of a given grade for a given subject.
* @param grade the grade level for which to generate the CSV
* @param subject the subject for which to generate the CSV
* @return a CSV string representing the results of each student for this subject across all classes of the specified grade
*/
public static String getResultsCSV(int grade, Subject subject) {
StringBuilder csvBuilder = new StringBuilder();
List<SchoolClass> classes = getAll().stream().filter(c -> c.getGrade() == grade).toList();

csvBuilder.append("Class,Student ID,First Name,Last Name,Email,Total Score(%)\n");
for (SchoolClass schoolClass : classes) {
List<Student> students = schoolClass.getStudents();
for (Student student : students) {
double totalScore = student.getCurrentProgress(subject) * 100.;
csvBuilder.append(schoolClass.getLabel()).append(",")
.append(student.getId()).append(",")
.append(student.getFirstName()).append(",")
.append(student.getLastName()).append(",")
.append(student.getEmail()).append(",")
.append(String.format(Locale.US, "%.2f", totalScore)).append("\n");
}
}

return csvBuilder.toString();
}

}
59 changes: 59 additions & 0 deletions src/main/java/de/igslandstuhl/database/api/Student.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
Expand Down Expand Up @@ -404,6 +405,15 @@ public Set<SubjectRequest> getCurrentRequests(Subject subject) {
*/
public SchoolClass getSchoolClass() { return schoolClass; }

/**
* Checks if the student has completed a specific task.
* @param task the task to check
* @return true if the student has completed the task, false otherwise
*/
public boolean hasCompletedTask(Task task) {
return completedTasks.contains(task);
}

/**
* Adds a subject request for this student.
* @param subjectId the subject ID
Expand Down Expand Up @@ -801,4 +811,53 @@ public Student setPassword(String password) throws SQLException {
students.remove(id);
return get(id);
}

/**
* Generates a CSV string containing the student's results.
* @return a CSV string of the student's results
*/
public String getResultsCSV() {
StringBuilder csvBuilder = new StringBuilder();
csvBuilder.append("Subject,Current Progress (%),Predicted Progress (%),Currently Achieved Grade,Predicted Grade\n");
for (Subject subject : getSchoolClass().getSubjects()) {
double currentProgress = getCurrentProgress(subject) * 100;
double predictedProgress = getPredictedProgress(subject) * 100;
int currentGrade = getCurrentlyAchievedGrade(subject);
int predictedGrade = getPredictedGrade(subject);
csvBuilder.append(String.format(Locale.US, "%s,%.2f,%.2f,%d,%d\n",
subject.getName(),
currentProgress,
predictedProgress,
currentGrade,
predictedGrade));
}
return csvBuilder.toString();
}
/**
* Generates a CSV string containing all students' results.
* @return a CSV string of all students' results
*/
public static String getAllResultsCSV() {
StringBuilder csvBuilder = new StringBuilder();
csvBuilder.append("Student ID,First Name,Last Name,Email,Subject,Current Progress (%),Predicted Progress (%),Currently Achieved Grade,Predicted Grade\n");
for (Student student : getAll()) {
for (Subject subject : student.getSchoolClass().getSubjects()) {
double currentProgress = student.getCurrentProgress(subject) * 100;
double predictedProgress = student.getPredictedProgress(subject) * 100;
int currentGrade = student.getCurrentlyAchievedGrade(subject);
int predictedGrade = student.getPredictedGrade(subject);
csvBuilder.append(String.format(Locale.US, "%d,%s,%s,%s,%s,%.2f,%.2f,%d,%d\n",
student.getId(),
student.getFirstName(),
student.getLastName(),
student.getEmail(),
subject.getName(),
currentProgress,
predictedProgress,
currentGrade,
predictedGrade));
}
}
return csvBuilder.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.igslandstuhl.database.api.modules;

public class BoolSetting extends ModuleSetting<Boolean> {
public BoolSetting(String key, String name, String description, boolean defaultValue) {
super(key, name, description, defaultValue);
}

public void toggle() {
setValue(!getValue());
}
public void enable() {
setValue(true);
}
public void disable() {
setValue(false);
}
public boolean isEnabled() {
return getValue();
}

@Override
public String toJSON() {
return "{" +
"\"key\":\"" + getKey() + "\"," +
"\"name\":\"" + getName() + "\"," +
"\"description\":\"" + getDescription() + "\"," +
"\"defaultValue\":" + getDefaultValue() + "," +
"\"value\":" + getValue() +
"}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package de.igslandstuhl.database.api.modules;

public class ModuleSetting<T> {
private final String key;
private final String name;
private final String description;
private final T defaultValue;
private T value;

public ModuleSetting(String key, String name, String description, T defaultValue) {
this.key = key;
this.name = name;
this.description = description;
this.defaultValue = defaultValue;
this.value = defaultValue;
}

public String getKey() {
return key;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public T getDefaultValue() {
return defaultValue;
}

public T getValue() {
return value;
}

public void setValue(T value) {
this.value = value;
}

public String toJSON() {
return "{" +
"\"key\":\"" + key + "\"," +
"\"name\":\"" + name + "\"," +
"\"description\":\"" + description + "\"," +
"\"defaultValue\":\"" + defaultValue + "\"," +
"\"value\":\"" + value + "\"" +
"}";
}

}
Loading
Loading