Skip to content

Commit

Permalink
Add files manually to resolve merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
edkaya committed Jun 20, 2024
1 parent b5cce62 commit 843c23d
Show file tree
Hide file tree
Showing 7 changed files with 510 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
@if (studentExam) {
<div (click)="openStudentExam()">
<!-- To clearly indicate a StudentExam within the working time, which can be resumed, the card should be displayed in blue -->
<div
[ngClass]="{
'row card-body justify-content-center card-general-settings': true,
'bg-primary text-white': withinWorkingTime,
clickable: withinWorkingTime || studentExam.submitted
}"
>
<div class="row">
<!-- Two variants: Play-Icon, if the studentExam is still within the working time and thus can be resumed.
Magnifying Class for finished StudentExams, to indicate the possibility to review the exam -->
<h4 class="col-sm-auto icon-settings">
@if (withinWorkingTime) {
<fa-icon [icon]="faCirclePlay" size="2x" />
}
@if (!withinWorkingTime && studentExam.submitted) {
<fa-icon [icon]="faMagnifyingGlass" size="2x" />
}
@if (!withinWorkingTime && !studentExam.submitted) {
<fa-icon [icon]="faFileCircleXmark" size="2x" />
}
</h4>
<div class="col-sm">
<div class="row">
<div class="col">
<h5 class="text-start">
{{ 'artemisApp.exam.overview.testExam.' + (withinWorkingTime ? 'resumeAttempt' : 'reviewAttempt') | artemisTranslate: { attempt: index } }}
</h5>
</div>
<div class="col-auto">
@if (withinWorkingTime) {
<div class="text-end">
{{ 'artemisApp.exam.overview.testExam.workingTimeLeft' | artemisTranslate }} {{ workingTimeLeftInSeconds() | artemisDurationFromSeconds: true }}
</div>
}
@if (studentExam.submitted) {
<div>
@if (studentExam.submissionDate) {
<div class="text-end">
{{ 'artemisApp.exam.overview.testExam.submissionDate' | artemisTranslate }} {{ studentExam.submissionDate | artemisDate }}
</div>
}
@if (studentExam.submissionDate && studentExam.startedDate) {
<div class="text-end">
{{ 'artemisApp.exam.overview.testExam.workingTimeCalculated' | artemisTranslate }}
<jhi-testexam-working-time [studentExam]="studentExam" />
</div>
}
</div>
}
<!-- test exams have to be submitted by the students, just as it is the case with real exams -->
@if (!withinWorkingTime && !studentExam.submitted) {
<div>
<div class="text-end">{{ 'artemisApp.exam.overview.testExam.notSubmitted' | artemisTranslate }}</div>
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.icon-settings {
display: flex;
justify-content: left;
align-items: center;
min-height: 50px;
}

.card-general-settings {
padding: 5px 0;
border: 1px;
border-color: var(--primary);
border-radius: 3px;
transition: box-shadow 0.1s linear;

.row {
min-height: 35px;
align-items: center;
}

&:hover {
box-shadow: 0 2px 4px 0 var(--primary);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { faCirclePlay, faFileCircleXmark, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';
import { StudentExam } from 'app/entities/student-exam.model';
import dayjs from 'dayjs/esm';
import { Exam } from 'app/entities/exam.model';
import { Subscription, interval } from 'rxjs';

@Component({
selector: 'jhi-course-exam-attempt-review-detail',
templateUrl: './course-exam-attempt-review-detail.component.html',
styleUrls: ['./course-exam-attempt-review-detail.component.scss'],
})
export class CourseExamAttemptReviewDetailComponent implements OnInit, OnDestroy {
@Input() studentExam: StudentExam;
// Both needed for routing (and for the exam.workingTime)
@Input() exam: Exam;
@Input() courseId: number;
// Index used to enumerate the attempts per student
@Input() index: number;
@Input() latestExam: boolean;
studentExamState: Subscription;

// Helper-Variables
withinWorkingTime: boolean;

// Icons
faMagnifyingGlass = faMagnifyingGlass;
faCirclePlay = faCirclePlay;
faFileCircleXmark = faFileCircleXmark;

constructor(private router: Router) {}

/**
* Calculate the individual working time for every submitted StudentExam. As the StudentExam needs to be submitted, the
* working time cannot change.
* For the latest StudentExam, which is still within the allowed working time, a subscription is used to periodically check this.
*/
ngOnInit() {
if (this.studentExam.started && this.studentExam.submitted && this.studentExam.startedDate && this.studentExam.submissionDate) {
this.withinWorkingTime = false;
} else if (this.latestExam) {
// A subscription is used here to limit the number of calls for the countdown of the remaining workingTime.
this.studentExamState = interval(1000).subscribe(() => {
this.isWithinWorkingTime();
// If the StudentExam is no longer within the working time, the subscription can be unsubscribed, as the state will not change anymore
if (!this.withinWorkingTime) {
this.unsubscribeFromExamStateSubscription();
}
});
} else {
this.withinWorkingTime = false;
}
}

ngOnDestroy() {
this.unsubscribeFromExamStateSubscription();
}

/**
* Used to unsubscribe from the studentExamState Subscriptions
*/
unsubscribeFromExamStateSubscription() {
this.studentExamState?.unsubscribe();
}

/**
* Determines if the given StudentExam is (still) within the working time
*/
isWithinWorkingTime() {
if (this.studentExam.started && !this.studentExam.submitted && this.studentExam.startedDate && this.exam.workingTime) {
const endDate = dayjs(this.studentExam.startedDate).add(this.exam.workingTime, 'seconds');
this.withinWorkingTime = dayjs(endDate).isAfter(dayjs());
}
}

/**
* Dynamically calculates the remaining working time of an attempt, if the attempt is started, within the working time and not yet submitted
*/
workingTimeLeftInSeconds(): number {
if (this.studentExam.started && !this.studentExam.submitted && this.studentExam.startedDate && this.exam.workingTime) {
return this.studentExam.startedDate.add(this.exam.workingTime, 'seconds').diff(dayjs(), 'seconds');
}
return 0;
}

/**
* navigate to /courses/:courseId/exams/:examId/test-exam/:studentExamId
* Used to open the corresponding studentExam
*/
openStudentExam(): void {
if (this.studentExam.submitted || this.withinWorkingTime) {
this.router.navigate(['courses', this.courseId, 'exams', this.exam.id, 'test-exam', this.studentExam.id]);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
@if (exam) {
<div [ngClass]="{ clickable: !maxAttemptsReached && !(exam.testExam && examState === 'CLOSED') }" (click)="openExam()" id="exam-{{ exam.id }}">
<!-- Signature colors for the header: Blue for test exams and Green for RedExams -->
<div
[ngClass]="{
'row card-header': true,
'bg-primary': exam.testExam,
'bg-success': !exam.testExam
}"
>
<h5 class="text-center text-white">{{ exam.title }}</h5>
</div>
<!-- Body of the Component. Content changes depending on the exam state-->
<div class="row d-flex justify-content-center">
@if (examState === 'UNDEFINED') {
<h4 class="icon-settings">
<!-- Case 7: Undefined exam state. show Pencil without further information -->
<fa-icon [icon]="faPenAlt" size="3x" />
</h4>
} @else {
<div class="col-3 icon-settings my-3">
@switch (examState) {
<!-- Calender to indicate an upcoming exam -->
@case ('UPCOMING') {
<fa-icon [icon]="faCalendarDay" size="2x" />
}
<!-- Play Button without circle to indicate, the exam starts shortly -->
@case ('IMMINENT') {
<fa-icon [icon]="faPlay" size="2x" />
}
<!-- Play-Button to indicate, the exam can be started -->
@case ('CONDUCTING') {
<fa-icon [icon]="faCirclePlay" size="2x" />
}
<!-- User with a clock to indicate indivial time extensions -->
@case ('TIMEEXTENSION') {
<fa-icon [icon]="faUserClock" size="2x" />
}
<!-- Magnifying Glass to indicate, the exam can be reviewed -->
@case ('STUDENTREVIEW') {
<fa-icon [icon]="faMagnifyingGlass" size="2x" />
}
<!-- Book to indicate, the exam is closed -->
@case ('CLOSED') {
<fa-icon [icon]="faBook" size="2x" />
}
<!-- Stop to indicate, no more attemps are possible -->
@case ('NO_MORE_ATTEMPTS') {
<fa-icon [icon]="faCircleStop" size="2x" />
}
}
</div>
<div class="col-9 row justify-content-center align-content-center">
@switch (examState) {
@case ('UPCOMING') {
<div>
<h5 class="text-center">{{ 'artemisApp.exam.overview.' + (exam.testExam ? 'testExam.' : '') + 'upcoming' | artemisTranslate }}</h5>
<div class="text-center">
{{ 'artemisApp.exam.overview.' + (exam.testExam ? 'testExam.' : '') + 'imminent' | artemisTranslate }}
{{ timeLeftToStart | artemisDurationFromSeconds }}
</div>
</div>
}
@case ('IMMINENT') {
<div>
<h5 class="text-center">
{{ 'artemisApp.exam.overview.' + (exam.testExam ? 'testExam.' : '') + 'imminent' | artemisTranslate }}
{{ timeLeftToStart | artemisDurationFromSeconds }}
</h5>
<div class="text-center">{{ 'artemisApp.exam.overview.' + (exam.testExam ? 'testExam.' : '') + 'imminentExplanation' | artemisTranslate }}</div>
</div>
}
@case ('CONDUCTING') {
<div>
<h5 class="text-center">{{ 'artemisApp.exam.overview.' + (exam.testExam ? 'testExam.' : '') + 'conducting' | artemisTranslate }}</h5>
</div>
}
@case ('TIMEEXTENSION') {
<div>
<h5 class="text-center">{{ 'artemisApp.exam.overview.timeExtension' | artemisTranslate }}</h5>
<div class="text-center">{{ 'artemisApp.exam.overview.timeExtensionExplanation' | artemisTranslate }}</div>
</div>
}
@case ('CLOSED') {
<div>
<h5 class="text-center">{{ 'artemisApp.exam.overview.' + (exam.testExam ? 'testExam.' : '') + 'closed' | artemisTranslate }}</h5>
</div>
}
@case ('STUDENTREVIEW') {
<div>
<h5 class="text-center">{{ 'artemisApp.exam.overview.review' | artemisTranslate }}</h5>
<div class="text-center">{{ 'artemisApp.exam.overview.reviewExplanation' | artemisTranslate }} {{ exam.examStudentReviewEnd | artemisDate }}</div>
</div>
}
@case ('NO_MORE_ATTEMPTS') {
<div>
<h5 class="text-center">{{ 'artemisApp.exam.overview.testExam.noMoreAttempts' | artemisTranslate }}</h5>
<div class="text-center">{{ 'artemisApp.exam.overview.testExam.noMoreAttemptsExplanation' | artemisTranslate }}</div>
</div>
}
}
</div>
}
</div>
<!-- Footer -->
<div class="card-footer row">
<!-- For real exams, the start date is shown. For test exams, the working window is shown to the students -->
@if (!exam.testExam && exam.startDate) {
<div class="col-sm">{{ 'artemisApp.exam.overview.start' | artemisTranslate: { start: exam.startDate | artemisDate } }}</div>
}
@if (exam.testExam && exam.startDate && exam.endDate) {
<div class="col-12">
{{
'artemisApp.exam.overview.testExam.available'
| artemisTranslate
: {
startDate: exam.startDate | artemisDate,
endDate: exam.endDate | artemisDate
}
}}
</div>
}
@if (exam.startDate && exam.endDate) {
<div class="col-12">{{ 'artemisApp.exam.overview.duration' | artemisTranslate }} {{ exam.workingTime! | artemisDurationFromSeconds: true }}</div>
}
@if (exam.examMaxPoints) {
<div class="col-sm">{{ 'artemisApp.exam.overview.maxPoints' | artemisTranslate: { points: exam.examMaxPoints } }}</div>
}
</div>
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.icon-settings {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}

.card-general-settings {
padding: 5px 0;
border: 1px;
border-radius: 3px;
transition: box-shadow 0.1s linear;
}
Loading

0 comments on commit 843c23d

Please sign in to comment.