Skip to content

Commit

Permalink
Dev/41: Extend ExamOverview with Verdict (Best Record of the question) (
Browse files Browse the repository at this point in the history
#44)

* Extend ExamOverview with Verdict (Best Record of the question)

* Improve from review

* ✨ Exam -> Question -> Answer works

1. Add `routePrefixing` as a route's data for multi-tabs-panel.component.ts & code-upload-panel.component.ts
2. Fix dependency injection issue (submissions page is buggy) of Submission-Service
3. Clean Code & avoid memory leak (by unsubscribing subjects) in submissions.component.ts
4. Add EventBus for VerdictIssuedEvent
  • Loading branch information
Johnny850807 authored Apr 15, 2021
1 parent abfa3fe commit 90a56ff
Show file tree
Hide file tree
Showing 23 changed files with 446 additions and 276 deletions.
52 changes: 29 additions & 23 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,57 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {Params, RouterModule, Routes} from '@angular/router';
import {LoginComponent} from './users/login/login.component';
import {ProblemListComponent} from './problem-list/problem-list.component';
import { ExamListComponent } from './exam/exam-list/exam-list.component';
import { ExamHomeComponent } from './exam/exam-home/exam-home.component';
import { ExamProblemsComponent } from './exam/exam-problems/exam-problems.component';
import { ExamSubmissionsComponent } from './exam/exam-submissions/exam-submissions.component';
import { ExamScoreboardComponent } from './exam/exam-scoreboard/exam-scoreboard.component';
import {ExamListComponent} from './exam/exam-list/exam-list.component';
import {ExamHomeComponent} from './exam/exam-home/exam-home.component';
import {ExamQuestionsComponent} from './exam/exam-questions/exam-questions.component';
import {ExamSubmissionsComponent} from './exam/exam-submissions/exam-submissions.component';
import {ExamScoreboardComponent} from './exam/exam-scoreboard/exam-scoreboard.component';
import {MultiTabsPanelComponent} from './problem-submission-tab-panel/multi-tabs-panel.component';
import {SubmissionsComponent} from './submissions/submissions.component';
import {ProblemDescriptionComponent} from './problem-description/problem-description.component';
import {TestcasesComponent} from './testcases/testcases.component';
import { ChangePasswordComponent } from './users/change-password/change-password.component';
import {ChangePasswordComponent} from './users/change-password/change-password.component';

const routes: Routes = [
{path: '', component: LoginComponent},
{ path: 'users/change-password', component: ChangePasswordComponent },
{path: 'users/change-password', component: ChangePasswordComponent},
{path: 'problems', component: ProblemListComponent},
{ path: 'exams/:examId/questions/:questionId', component: MultiTabsPanelComponent },
{ path: 'exams', component: ExamListComponent, },
{path: 'exams/:examId/questions/:questionId', component: MultiTabsPanelComponent},
{path: 'exams', component: ExamListComponent},
{
path: 'exams/:examId', component: ExamHomeComponent,
children: [
{ path: '', component: ExamProblemsComponent, },
{ path: 'submissions', component: ExamSubmissionsComponent, },
{ path: 'scoreboard', component: ExamScoreboardComponent, },
{path: '', component: ExamQuestionsComponent},
{path: 'submissions', component: ExamSubmissionsComponent},
{path: 'scoreboard', component: ExamScoreboardComponent},
],
},
{
path: 'problems/:problemId', component: MultiTabsPanelComponent,
children: [
{ path: '', component: ProblemDescriptionComponent },
{ path: 'description', component: ProblemDescriptionComponent },
{ path: 'testcases', component: TestcasesComponent },
{ path: 'submissions', component: SubmissionsComponent }
{path: '', component: ProblemDescriptionComponent},
{path: 'description', component: ProblemDescriptionComponent},
{path: 'testcases', component: TestcasesComponent},
{path: 'submissions', component: SubmissionsComponent}
],
data: { submissionService: 'SUBMISSION_SERVICE' }
data: {
submissionService: 'SUBMISSION_SERVICE',
routePrefixing: () => ''
}
},
{
path: 'exams/:examId/problems/:problemId', component: MultiTabsPanelComponent,
children: [
{ path: '', component: ProblemDescriptionComponent },
{ path: 'description', component: ProblemDescriptionComponent },
{ path: 'testcases', component: TestcasesComponent },
{ path: 'submissions', component: SubmissionsComponent }
{path: '', component: ProblemDescriptionComponent},
{path: 'description', component: ProblemDescriptionComponent},
{path: 'testcases', component: TestcasesComponent},
{path: 'submissions', component: SubmissionsComponent}
],
data: { submissionService: 'EXAM_QUESTION_SUBMISSION_SERVICE' }
data: {
submissionService: 'EXAM_QUESTION_SUBMISSION_SERVICE',
routePrefixing: (routeParams: Params) => `/exams/${routeParams.examId}/`
}
},
];

Expand Down
28 changes: 19 additions & 9 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import {Component, OnInit} from '@angular/core';
import {Component, OnDestroy, OnInit} from '@angular/core';
import {MessageService} from 'primeng';
import {BrokerService, StudentService, SubmissionService} from './services/Services';
import {JudgeStatus, VerdictIssuedEvent} from './models';
import {CookieService} from './services/cookie/cookie.service';
import {Router} from '@angular/router';
import {ActivatedRoute, Router} from '@angular/router';
import {EventBus, EventSubscriber} from './services/EventBus';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
export class AppComponent extends MessageService implements OnInit, OnDestroy, EventSubscriber {
readonly MESSAGE_KEY_SUBMISSION_TOAST = 'submission-toast-key';

isLoggedIn = false;

constructor(private submissionService: SubmissionService,
public studentService: StudentService,
constructor(public studentService: StudentService,
private cookieService: CookieService,
private messageService: MessageService,
private brokerService: BrokerService,
private eventBus: EventBus,
private router: Router) {
super();
}

ngOnInit(): void {
this.submissionService.verdictIssuedEventObservable.subscribe(
(verdictIssuedEvent) => this.onVerdictIssued(verdictIssuedEvent));

this.eventBus.subscribe(this);
this.studentService.tryAuthWithCurrentToken().subscribe(isLoggedIn => {
this.isLoggedIn = isLoggedIn;
if (isLoggedIn) {
Expand All @@ -37,7 +37,17 @@ export class AppComponent implements OnInit {
});
}

private onVerdictIssued(event: VerdictIssuedEvent) {
ngOnDestroy(): void {
this.eventBus.unsubscribe(this);
}

onEvent(event: any) {
if (event instanceof VerdictIssuedEvent) {
this.issueVerdict(event);
}
}

issueVerdict(event: VerdictIssuedEvent) {
this.messageService.add({
key: this.MESSAGE_KEY_SUBMISSION_TOAST,
severity: event.verdict.summaryStatus === JudgeStatus.AC ? 'success' : 'error',
Expand Down
13 changes: 7 additions & 6 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {AppComponent} from './app.component';
import {ProblemListComponent} from './problem-list/problem-list.component';
import {ExamListComponent} from './exam/exam-list/exam-list.component';
import {ExamHomeComponent} from './exam/exam-home/exam-home.component';
import {ExamProblemsComponent} from './exam/exam-problems/exam-problems.component';
import {ExamQuestionsComponent} from './exam/exam-questions/exam-questions.component';
import {ExamScoreboardComponent} from './exam/exam-scoreboard/exam-scoreboard.component';
import {ExamSubmissionsComponent} from './exam/exam-submissions/exam-submissions.component';
import {BrokerService, ExamService, ProblemService, StudentService, SubmissionService} from './services/Services';
Expand All @@ -33,7 +33,8 @@ import {ChangePasswordComponent} from './users/change-password/change-password.c
import {FormsModule} from '@angular/forms';
import {StompBrokerService} from './services/impl/StompBrokerService';
import {RxStompConfig} from '@stomp/rx-stomp';
import { HttpExamQuestionSubmissionService } from './services/impl/HttpExamQuestionSubmissionService';
import {HttpExamQuestionSubmissionService} from './services/impl/HttpExamQuestionSubmissionService';
import {EventBus} from './services/EventBus';


const DOMAIN = 'api.judgegirl.beta.pdlab.csie.ntu.edu.tw';
Expand All @@ -57,7 +58,7 @@ rxStompConfig.reconnectDelay = 200;
SubmissionsComponent,

ExamHomeComponent,
ExamProblemsComponent,
ExamQuestionsComponent,
ExamSubmissionsComponent,
ExamScoreboardComponent,

Expand Down Expand Up @@ -88,11 +89,11 @@ rxStompConfig.reconnectDelay = 200;
{provide: StudentService, useClass: HttpStudentService},
{provide: ProblemService, useClass: HttpProblemService},
{provide: ExamService, useClass: HttpExamService},
{provide: SubmissionService, useClass: HttpSubmissionService},
{provide: BrokerService, useClass: StompBrokerService},
{provide: RxStompConfig, useValue: rxStompConfig},
{ provide: 'SUBMISSION_SERVICE', useClass: HttpSubmissionService },
{ provide: 'EXAM_QUESTION_SUBMISSION_SERVICE', useClass: HttpExamQuestionSubmissionService },
{provide: EventBus, useClass: EventBus},
{provide: 'SUBMISSION_SERVICE', useClass: HttpSubmissionService},
{provide: 'EXAM_QUESTION_SUBMISSION_SERVICE', useClass: HttpExamQuestionSubmissionService},
{provide: 'STUDENT_SERVICE_BASE_URL', useValue: `${HTTP_HOST}`},
{provide: 'PROBLEM_SERVICE_BASE_URL', useValue: `${HTTP_HOST}`},
{provide: 'SUBMISSION_SERVICE_BASE_URL', useValue: `${HTTP_HOST}`},
Expand Down
9 changes: 0 additions & 9 deletions src/app/code-panel/code-upload-panel.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,4 @@ <h3>Submissions</h3>
</div>
</div>
</form>

<hr>
<span class="feedback">The beta version of Judge Girl (K8S) doesn't support code panels. </span>
<br>
<span class="feedback">Feel free to give any feedback via </span>
<a class="feedback" href="https://github.com/Judge-Girl/Judge-K8S-Web">github</a> <span>.</span>
<br>
<span class="feedback">Wanna contribute? Feel free to tell the TA or </span>
<a class="feedback" href = "mailto: johnny850807@gmail.com">johnny850807@gmail.com</a>
</div>
16 changes: 11 additions & 5 deletions src/app/code-panel/code-upload-panel.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Component, Injector, OnInit, ViewChildren } from '@angular/core';
import {Component, Injector, OnInit, ViewChildren} from '@angular/core';
import {FileUpload, MessageService} from 'primeng';
import {ProblemService, StudentService, SubmissionService, SubmissionThrottlingError} from '../services/Services';
import {getCodeFileExtension, Problem, SubmittedCodeSpec} from '../models';
import {switchMap} from 'rxjs/operators';
import {ActivatedRoute, Router} from '@angular/router';
import {ActivatedRoute, Params, Router} from '@angular/router';
import {Observable} from 'rxjs';

@Component({
Expand All @@ -21,15 +21,21 @@ export class CodeUploadPanelComponent implements OnInit {
@ViewChildren('fileInput') private fileUploads: FileUpload[];

submissionService: SubmissionService;
private routeParams: Params;
private routePrefixing: (routeParams: Params) => string;

constructor(public studentService: StudentService,
private problemService: ProblemService,
// private submissionService: SubmissionService,
private route: ActivatedRoute,
private messageService: MessageService,
private injector: Injector,
private router: Router) {
const submissionServiceInstanceName = route.snapshot.data['submissionService'];
route.params.subscribe(params => {
this.routeParams = params;
});
this.routePrefixing = route.snapshot.data.routePrefixing;

const submissionServiceInstanceName = route.snapshot.data.submissionService;
this.submissionService = injector.get<SubmissionService>(submissionServiceInstanceName);
}

Expand Down Expand Up @@ -66,7 +72,7 @@ export class CodeUploadPanelComponent implements OnInit {

submit(): boolean {
if (this.validateAllSpecifiedFileSelected()) {
this.router.navigateByUrl(`/problems/${this.problem.id}/submissions`);
this.router.navigateByUrl(`${this.routePrefixing(this.routeParams)}problems/${this.problem.id}/submissions`);
this.submissionService.submitFromFile(this.problem.id, this.selectedFiles)
.toPromise().catch(err => this.handleSubmitError(err));
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/exam/exam-home/exam-home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/
</span>
<span *ngIf="!isLoading" (click)="routeToCurrentExamIndex()" class="fake-link">
{{ exam.name }}
{{ examOverview.name }}
</span>
</div>
</div>
Expand Down
16 changes: 9 additions & 7 deletions src/app/exam/exam-home/exam-home.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, NavigationEnd, Router} from '@angular/router';
import {ExamService, StudentService} from '../../services/Services';
import {Exam} from '../../models';
import {ExamOverview} from '../../models';
import {SplitComponent} from 'angular-split';

export enum Tab {
Expand All @@ -15,8 +15,10 @@ export enum Tab {
})
export class ExamHomeComponent implements OnInit, AfterViewInit {

constructor(public studentService: StudentService,
public examService: ExamService,

constructor(private elementRef: ElementRef,
private studentService: StudentService,
private examService: ExamService,
private router: Router, private route: ActivatedRoute) {
route.params.subscribe(params => this.examId = +params.examId);

Expand All @@ -34,14 +36,14 @@ export class ExamHomeComponent implements OnInit, AfterViewInit {

private examId: number;
public isLoading: boolean;
public exam: Exam;
public examOverview: ExamOverview;

ngOnInit(): void {
this.isLoading = true;
if (this.studentService.authenticate()) {
this.examService.getExamOverview(this.examId)
this.examService.getExamProgressOverview(this.studentService.currentStudent.id, this.examId)
.subscribe(e => {
this.exam = e;
this.examOverview = e;
this.isLoading = false;
});
}
Expand Down Expand Up @@ -106,7 +108,7 @@ export class ExamHomeComponent implements OnInit, AfterViewInit {
}

routeToCurrentExamIndex() {
this.router.navigateByUrl(`/exams/${this.exam.id}`);
this.router.navigateByUrl(`/exams/${this.examOverview.id}`);
this.refreshTabElementsState();
}
}
Expand Down
47 changes: 0 additions & 47 deletions src/app/exam/exam-problems/exam-problems.component.html

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,27 @@ tfoot {
}

.table td, .table th {
border-top: 0px;
}
border-top: 0;
vertical-align: middle;
}

.judge-status {
font-size: 20px;
font-weight: 600;
}

.judge-status.AC {
color: #91CB46;
}

.judge-status.WA, .judge-status.RE, .judge-status.SYSTEM_ERR {
color: #F82727;
}

.judge-status.TLE, .judge-status.MLE, .judge-status.OLE {
color: #1273BA;
}

.judge-status.CE {
color: #F2B311;
}
Loading

0 comments on commit 90a56ff

Please sign in to comment.