diff --git a/src/main/java/de/tum/cit/ase/service/SimulationService.java b/src/main/java/de/tum/cit/ase/service/SimulationService.java index 807c259b..818ca169 100644 --- a/src/main/java/de/tum/cit/ase/service/SimulationService.java +++ b/src/main/java/de/tum/cit/ase/service/SimulationService.java @@ -46,69 +46,76 @@ public synchronized void simulateExam( long courseId, long examId, ArtemisServer server, + boolean noPreparation, ArtemisAccountDTO artemisAccountDTO ) { boolean cleanupNeeded = false; - ArtemisAdmin admin; + ArtemisAdmin admin = null; logAndSendInfo("Starting simulation with %d users on %s...", numberOfUsers, server.name()); - try { - logAndSendInfo("Initializing admin..."); - admin = server == ArtemisServer.PRODUCTION ? initializeAdminWithAccount(server, artemisAccountDTO) : initializeAdmin(server); - } catch (Exception e) { - logAndSendError("Error while initializing admin: %s", e.getMessage()); - simulationWebsocketService.sendSimulationFailed(); - return; - } - - if (courseId == 0L && examId == 0L) { - logAndSendInfo("No course and exam specified. Creating course and exam..."); - cleanupNeeded = true; - Course course; - - // Create course + if (!noPreparation) { try { - course = admin.createCourse(); + logAndSendInfo("Initializing admin..."); + admin = + server == ArtemisServer.PRODUCTION ? initializeAdminWithAccount(server, artemisAccountDTO) : initializeAdmin(server); } catch (Exception e) { - logAndSendError("Error while creating course: %s", e.getMessage()); + logAndSendError("Error while initializing admin: %s", e.getMessage()); simulationWebsocketService.sendSimulationFailed(); return; } - logAndSendInfo("Successfully created course. Course ID: %d", course.getId()); - // Create exam + + if (courseId == 0L && examId == 0L) { + logAndSendInfo("No course and exam specified. Creating course and exam..."); + cleanupNeeded = true; + Course course; + + // Create course + try { + course = admin.createCourse(); + } catch (Exception e) { + logAndSendError("Error while creating course: %s", e.getMessage()); + simulationWebsocketService.sendSimulationFailed(); + return; + } + logAndSendInfo("Successfully created course. Course ID: %d", course.getId()); + // Create exam + try { + var exam = createAndInitializeExam(numberOfUsers, server, admin, course); + courseId = exam.getCourse().getId(); + examId = exam.getId(); + } catch (Exception e) { + logAndSendError("Error while creating exam: %s", e.getMessage()); + ArtemisAdmin finalAdmin = admin; + new Thread(() -> cleanup(finalAdmin, course.getId())).start(); + simulationWebsocketService.sendSimulationFailed(); + return; + } + logAndSendInfo("Successfully initialized exam. Exam ID: %d", examId); + } else { + logAndSendInfo("Using existing course %d and exam %d.", courseId, examId); + } try { - var exam = createAndInitializeExam(numberOfUsers, server, admin, course); - courseId = exam.getCourse().getId(); - examId = exam.getId(); + logAndSendInfo("Preparing exam for simulation..."); + admin.prepareExam(courseId, examId); } catch (Exception e) { - logAndSendError("Error while creating exam: %s", e.getMessage()); - cleanup(admin, course.getId()); + logAndSendError("Error while preparing exam: %s", e.getMessage()); + if (cleanupNeeded) { + ArtemisAdmin finalAdmin1 = admin; + long finalCourseId1 = courseId; + new Thread(() -> cleanup(finalAdmin1, finalCourseId1)).start(); + } simulationWebsocketService.sendSimulationFailed(); return; } - logAndSendInfo("Successfully initialized exam. Exam ID: %d", examId); - } else { - logAndSendInfo("Using existing course %d and exam %d.", courseId, examId); - } - try { - logAndSendInfo("Preparing exam for simulation..."); - admin.prepareExam(courseId, examId); - } catch (Exception e) { - logAndSendError("Error while preparing exam: %s", e.getMessage()); - if (cleanupNeeded) { - cleanup(admin, courseId); - } - simulationWebsocketService.sendSimulationFailed(); - return; - } - logAndSendInfo("Preparation finished..."); - try { - // Wait for a couple of seconds. Without this, students cannot access their repos. - // Not sure why this is necessary, trying to figure it out - sleep(5_000); - } catch (InterruptedException ignored) {} + logAndSendInfo("Preparation finished..."); + try { + // Wait for a couple of seconds. Without this, students cannot access their repos. + // Not sure why this is necessary, trying to figure it out + sleep(5_000); + } catch (InterruptedException ignored) {} + } logAndSendInfo("Starting simulation..."); @@ -143,7 +150,9 @@ public synchronized void simulateExam( } catch (Exception e) { logAndSendError("Error while performing simulation: %s", e.getMessage()); if (cleanupNeeded) { - cleanup(admin, courseId); + ArtemisAdmin finalAdmin2 = admin; + long finalCourseId2 = courseId; + new Thread(() -> cleanup(finalAdmin2, finalCourseId2)).start(); } simulationWebsocketService.sendSimulationFailed(); return; @@ -156,7 +165,9 @@ public synchronized void simulateExam( simulationWebsocketService.sendSimulationResult(simulationResult); if (cleanupNeeded) { simulationWebsocketService.sendSimulationError("The result is available, but please note that the cleanup is still running!"); - cleanup(admin, courseId); + ArtemisAdmin finalAdmin3 = admin; + long finalCourseId3 = courseId; + new Thread(() -> cleanup(finalAdmin3, finalCourseId3)).start(); } simulationWebsocketService.sendSimulationCompleted(); } @@ -182,7 +193,7 @@ private Exam createAndInitializeExam(int numberOfUsers, ArtemisServer server, Ar logAndSendInfo("Successfully created course and exam. Waiting for synchronization of user groups (1 min)..."); try { - sleep(1000 * 60); //Wait for 1 minutes until user groups are synchronized + sleep(1000 * 60); //Wait for 1 minute until user groups are synchronized } catch (InterruptedException ignored) {} // Create exam exercises and register students @@ -233,16 +244,13 @@ private List performActionWithAll(int threadCount, int numberOfUser } private void cleanup(ArtemisAdmin admin, long courseId) { - logAndSendInfo("Cleaning up... Depending on the number of users, this may take a few minutes."); + logAndSendInfo("Cleaning up... Note that the cleanup will happen in 15min."); try { - sleep(1000 * 10); // Give the server a few seconds to recover + sleep(1000 * 60 * 15); // Wait 15min for builds to finish before deleting the course admin.deleteCourse(courseId); - logAndSendInfo("Successfully cleaned up."); + logAndSendInfo("Successfully cleaned up course %d.", courseId); } catch (Exception e) { - logAndSendError("Error while cleaning up: %s", e.getMessage()); - logAndSendError( - "The deletion of the course failed, potentially due to overloading of the Artemis Server. Please wait a few minutes and then delete the course 'Temporary Benchmarking Exam' manually. If the course is already deleted, make sure that the project 'benchmark Programming Exercise for Benchmarking' is deleted from the VCS as well." - ); + logAndSendError("Error while cleaning up course %d: %s", courseId, e.getMessage()); } } diff --git a/src/main/java/de/tum/cit/ase/web/rest/SimulationResource.java b/src/main/java/de/tum/cit/ase/web/rest/SimulationResource.java index 01bcea28..47954ebc 100644 --- a/src/main/java/de/tum/cit/ase/web/rest/SimulationResource.java +++ b/src/main/java/de/tum/cit/ase/web/rest/SimulationResource.java @@ -26,6 +26,7 @@ public ResponseEntity startSimulation( @RequestParam(value = "courseId") int courseId, @RequestParam(value = "examId") int examId, @RequestParam(value = "server") ArtemisServer server, + @RequestParam(value = "noPreparation") boolean noPreparation, @RequestBody(required = false) ArtemisAccountDTO artemisAccountDTO ) { if (numberOfUsers <= 0 || courseId < 0 || examId < 0 || server == null) { @@ -35,11 +36,14 @@ public ResponseEntity startSimulation( if ((courseId == 0) ^ (examId == 0)) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } - // Production only with admin account - if (server == ArtemisServer.PRODUCTION && artemisAccountDTO == null) { + // Production with preparation only with admin account + if (server == ArtemisServer.PRODUCTION && artemisAccountDTO == null && !noPreparation) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } - simulationService.simulateExam(numberOfUsers, courseId, examId, server, artemisAccountDTO); + if (courseId == 0 && noPreparation) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + simulationService.simulateExam(numberOfUsers, courseId, examId, server, noPreparation, artemisAccountDTO); return new ResponseEntity<>(HttpStatus.OK); } } diff --git a/src/main/webapp/app/simulations/simulations.component.html b/src/main/webapp/app/simulations/simulations.component.html index 042768b4..01c19bb7 100644 --- a/src/main/webapp/app/simulations/simulations.component.html +++ b/src/main/webapp/app/simulations/simulations.component.html @@ -14,7 +14,11 @@
- + +
+
+ +
@@ -24,11 +28,11 @@
-
+
-
+
@@ -63,9 +67,9 @@

The Benchmarking Tool will use an existing exam for the simulation. Please specify the IDs of course and exam and make sure that the necessary number of test-users (1 - {{ numberOfUsers }}) are registered for the exam. - Please be aware that: + Please be aware that:

-
    +
    • The start- and end-date of the exam will be changed. The change will NOT be undone after the simulation!
    • The student exams will be (re-)generated and prepared. This affects the exams of ALL registered students, not just the test-users! @@ -76,6 +80,9 @@ Make sure that no real students are registered!
    +

    + The Benchmarking Tool will not make any changes in the course and exam. Please make sure that the exam is fully prepared. +

    The Benchmarking Tool will simulate on the Artemis Production Instance! (https://artemis.cit.tum.de)
    - Since the tool does not have admin access on Production, you need to provide admin credentials. They will only be used for this one - simulation and not be stored. + Since the tool does not have admin access on Production, you need to provide admin credentials. They will only be used for this + one simulation and not be stored.

    Please note that performing a simulation with a large number of users can have a negative impact on the performance of the diff --git a/src/main/webapp/app/simulations/simulations.component.ts b/src/main/webapp/app/simulations/simulations.component.ts index b5730136..13d9ef0c 100644 --- a/src/main/webapp/app/simulations/simulations.component.ts +++ b/src/main/webapp/app/simulations/simulations.component.ts @@ -24,6 +24,7 @@ export class SimulationsComponent implements OnInit { adminPassword = ''; adminUsername = ''; availableServers = [ArtemisServer.TS1, ArtemisServer.TS3, ArtemisServer.PRODUCTION, ArtemisServer.STAGING]; + noPreparation = false; protected readonly ArtemisServer = ArtemisServer; @@ -64,6 +65,7 @@ export class SimulationsComponent implements OnInit { if (!this.useExistingExam) { this.courseId = 0; this.examId = 0; + this.noPreparation = false; } this.logMessages = []; const observer = { @@ -76,11 +78,11 @@ export class SimulationsComponent implements OnInit { }; let account; - if (this.selectedServer === ArtemisServer.PRODUCTION) { + if (this.selectedServer === ArtemisServer.PRODUCTION && !this.noPreparation) { account = new ArtemisAccountDTO(this.adminUsername, this.adminPassword); } this.simulationsService - .startSimulation(this.numberOfUsers, this.courseId, this.examId, this.selectedServer, account) + .startSimulation(this.numberOfUsers, this.courseId, this.examId, this.selectedServer, this.noPreparation, account) .subscribe(observer); } @@ -89,10 +91,17 @@ export class SimulationsComponent implements OnInit { return ( this.numberOfUsers > 0 && (!this.useExistingExam || (this.courseId > 0 && this.examId > 0)) && - this.adminPassword.length > 0 && - this.adminUsername.length > 0 + ((this.adminPassword.length > 0 && this.adminUsername.length > 0) || this.noPreparation) ); } return this.numberOfUsers > 0 && (!this.useExistingExam || (this.courseId > 0 && this.examId > 0)); } + + useExistingExamChanged(): void { + if (!this.useExistingExam) { + this.courseId = 0; + this.examId = 0; + this.noPreparation = false; + } + } } diff --git a/src/main/webapp/app/simulations/simulations.service.ts b/src/main/webapp/app/simulations/simulations.service.ts index 22083aae..37e2165b 100644 --- a/src/main/webapp/app/simulations/simulations.service.ts +++ b/src/main/webapp/app/simulations/simulations.service.ts @@ -51,10 +51,20 @@ export class SimulationsService { courseId: number, examId: number, server: ArtemisServer, + noPreparation: boolean, account?: ArtemisAccountDTO, ): Observable { const endpoint = this.applicationConfigService.getEndpointFor( - '/api/simulations?users=' + numberOfUsers + '&courseId=' + courseId + '&examId=' + examId + '&server=' + server, + '/api/simulations?users=' + + numberOfUsers + + '&courseId=' + + courseId + + '&examId=' + + examId + + '&server=' + + server + + '&noPreparation=' + + noPreparation, ); if (!window.location.protocol.startsWith('https:')) { // Only send credentials over HTTPS