Skip to content

Commit

Permalink
Small improvements of modeling exercises (#311)
Browse files Browse the repository at this point in the history
* load submissions of participations eagerly to prevent additional database calls
* show name of the user that currently has the lock for an assessment
* scroll to top that the user recognizes the response message after submitting the assessment of an example submission
* do not set input model of Apollon upon saving to prevent popups from closing
* also display children of selected elements in assessment result view
* add instruction to example assessment view
  • Loading branch information
SiggZ authored and Stephan Krusche committed Apr 20, 2019
1 parent 7b05164 commit 9e4cd44
Show file tree
Hide file tree
Showing 11 changed files with 58 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public interface ParticipationRepository extends JpaRepository<Participation, Lo

Optional<Participation> findByExerciseIdAndStudentLogin(Long exerciseId, String username);

@Query("select distinct participation from Participation participation left join fetch participation.submissions where participation.exercise.id = :#{#exerciseId} and participation.student.login = :#{#username}")
Optional<Participation> findByExerciseIdAndStudentLoginWithEagerSubmissions(@Param("exerciseId") Long exerciseId, @Param("username") String username);

Participation findOneByExerciseIdAndStudentLoginAndInitializationState(Long exerciseId, String username, InitializationState state);

List<Participation> findByBuildPlanIdAndInitializationState(String buildPlanId, InitializationState state);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ public List<ModelingSubmission> getAllModelingSubmissionsByTutorForExercise(Long
@Transactional(rollbackFor = Exception.class)
public ModelingSubmission save(ModelingSubmission modelingSubmission, ModelingExercise modelingExercise, String username) {

Optional<Participation> optionalParticipation = participationService.findOneByExerciseIdAndStudentLoginAnyState(modelingExercise.getId(), username);
Optional<Participation> optionalParticipation =
participationService.findOneByExerciseIdAndStudentLoginWithEagerSubmissionsAnyState(modelingExercise.getId(), username);
if (!optionalParticipation.isPresent()) {
throw new EntityNotFoundException("No participation found for " + username + " in exercise with id " + modelingExercise.getId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,19 @@ public Optional<Participation> findOneByExerciseIdAndStudentLoginAnyState(Long e
return participationRepository.findByExerciseIdAndStudentLogin(exerciseId, username);
}

/**
* Get one participation (in any state) by its student and exercise with eager submissions.
*
* @param exerciseId the project key of the exercise
* @param username the username of the student
* @return the entity
*/
@Transactional(readOnly = true)
public Optional<Participation> findOneByExerciseIdAndStudentLoginWithEagerSubmissionsAnyState(Long exerciseId, String username) {
log.debug("Request to get Participation for User {} for Exercise with id: {}", username, exerciseId);
return participationRepository.findByExerciseIdAndStudentLoginWithEagerSubmissions(exerciseId, username);
}

/**
* Get all participations for the given student including all results
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
<button class="btn btn-secondary" (click)="back()">&larr;</button>
</div>

<div class="col-12 col-lg-7">
<div class="col-12 col-lg-7 d-flex flex-column justify-content-between">
<h2 jhiTranslate="arTeMiSApp.modelingExercise.exampleSubmissionForModelingExercise" [translateValues]="{ exerciseTitle: exercise?.title }">
Example Modeling Submission for Exercise {{exercise?.title}}
</h2>
<p *ngIf="assessmentMode" jhiTranslate="arTeMiSApp.exampleSubmission.assessmentInstruction">
Double-click on a model element to view and edit the element's assessment.
</p>
</div>

<div class="col-12 col-lg-4 text-right" *ngIf="isAtLeastInstructor && !readOnly && !toComplete">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ export class ExampleModelingSubmissionComponent implements OnInit {
}

checkAssessment() {
// scroll to top that the user definitely recognizes the response message (success OR score too low/high)
window.scroll(0, 0);
this.checkScoreBoundaries();
if (!this.assessmentsAreValid) {
this.jhiAlertService.error('arTeMiSApp.modelingAssessment.invalidAssessments');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ <h2 class="top-container">
</div>
<jhi-alert></jhi-alert>
<div class="control-container row-container">
<span *ngIf="!isAuthorized" class="text-danger ml-3" style="font-size: 65%">Submission locked by another user!</span>
<span *ngIf="!isAuthorized" class="text-danger ml-3" style="font-size: 65%"
jhiTranslate="modelingAssessmentEditor.assessmentLocked" [translateValues]="{ otherUser: result?.assessor?.firstName }">
Assessment locked by another user!
</span>
<button class="btn btn-primary" (click)="onSaveAssessment()" [disabled]="!assessmentsAreValid || !isAuthorized" [hidden]="result && result.rated">
<fa-icon icon="save"></fa-icon>
<span jhiTranslate="entity.action.save">Save</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,8 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component
}

/**
* This function initialized the Apollon editor depending on the submission status.
* If it was already submitted, the Apollon editor is loaded in Assessment read-only mode.
* Otherwise, it is loaded in the modeling mode and an auto save timer is started.
* This function sets and starts an auto-save timer that automatically saves changes
* to the model after at most 60 seconds.
*/
setAutoSaveTimer(): void {
if (this.submission.submitted) {
Expand Down Expand Up @@ -216,7 +215,7 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component
return;
}
this.updateSubmissionModel();
if (!this.umlModel || this.umlModel.elements.length === 0) {
if (this.isModelEmpty(this.submission.model)) {
this.jhiAlertService.warning('arTeMiSApp.modelingEditor.empty');
return;
}
Expand Down Expand Up @@ -263,6 +262,11 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component
}
}

private isModelEmpty(model: string): boolean {
const umlModel: UMLModel = JSON.parse(model);
return !umlModel || !umlModel.elements || umlModel.elements.length === 0;
}

ngOnDestroy(): void {
this.subscription.unsubscribe();
clearInterval(this.autoSaveInterval);
Expand All @@ -275,8 +279,8 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component
* Updates the model of the submission with the current Apollon model state
*/
updateSubmissionModel(): void {
this.umlModel = this.modelingEditor.getCurrentModel();
const diagramJson = JSON.stringify(this.umlModel);
const umlModel = this.modelingEditor.getCurrentModel();
const diagramJson = JSON.stringify(umlModel);
if (this.submission && diagramJson != null) {
this.submission.model = diagramJson;
}
Expand All @@ -303,9 +307,23 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component
*/
onSelectionChanged(selection: Selection) {
this.selectedEntities = selection.elements;
for (const selectedEntity of this.selectedEntities) {
this.selectedEntities.push(...this.getSelectedChildren(selectedEntity));
}
this.selectedRelationships = selection.relationships;
}

/**
* Returns the elementIds of all the children of the element with the given elementId
* or an empty list, if no children exist for this element.
*/
private getSelectedChildren(elementId: string): string[] {
if (!this.umlModel || !this.umlModel.elements) {
return [];
}
return this.umlModel.elements.filter(element => element.owner === elementId).map(element => element.id);
}

/**
* Checks whether a model element in the modeling editor is selected.
*/
Expand Down
3 changes: 2 additions & 1 deletion src/main/webapp/i18n/de/exampleSubmission.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"score": "Punktzahl",
"submitAssessment": "Bewertung einreichen",
"readAndUnderstood": "Ich habe das Beispiel gelesen und verstanden.",
"exampleSubmission": "Beispielabgabe"
"exampleSubmission": "Beispielabgabe",
"assessmentInstruction": "Doppelklicken Sie auf ein Element im Modell, um dessen Bewertung zu öffnen und zu bearbeiten."
}
}
}
1 change: 1 addition & 0 deletions src/main/webapp/i18n/de/modelingAssessmentEditor.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"modelingAssessmentEditor": {
"assessment": "Bewertung",
"assessmentLocked": "Bewertung gesperrt von: {{otherUser}}",
"button": {
"overrideAssessment": "Bewertung überschreiben",
"nextSubmission": "Nächste Abgabe bewerten"
Expand Down
3 changes: 2 additions & 1 deletion src/main/webapp/i18n/en/exampleSubmission.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"score": "Score",
"submitAssessment": "Submit Assessment",
"readAndUnderstood": "I have read and understood the example",
"exampleSubmission": "Example Submission"
"exampleSubmission": "Example Submission",
"assessmentInstruction": "Double-click on a model element to view and edit the element's assessment."
}
}
}
1 change: 1 addition & 0 deletions src/main/webapp/i18n/en/modelingAssessmentEditor.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"modelingAssessmentEditor": {
"assessment": "Assessment",
"assessmentLocked": "Assessment locked by: {{otherUser}}",
"button": {
"overrideAssessment": "Override Assessment",
"nextSubmission": "Assess Next Submission"
Expand Down

0 comments on commit 9e4cd44

Please sign in to comment.