Spaces:
Running
Running
Ahmad Shallouf
commited on
Commit
•
27e40c0
1
Parent(s):
024b436
added new modifications
Browse files- .DS_Store +0 -0
- src/.DS_Store +0 -0
- src/app/.DS_Store +0 -0
- src/app/app.config.ts +4 -2
- src/app/app.routes.ts +5 -1
- src/app/components/.DS_Store +0 -0
- src/app/components/body/.DS_Store +0 -0
- src/app/components/body/body.component.html +1 -1
- src/app/components/body/control-panel/admin-login/admin-login.component.ts +12 -3
- src/app/components/body/control-panel/control-panel.component.html +0 -2
- src/app/components/body/control-panel/control-panel.component.ts +2 -2
- src/app/components/body/leaderboards/leaderboard/leaderboard.component.html +25 -43
- src/app/components/body/leaderboards/leaderboard/leaderboard.component.ts +20 -9
- src/app/components/body/submissions/submissions.component.html +26 -45
- src/app/components/body/submissions/submissions.component.ts +19 -5
- src/app/components/body/submitting/submitting-guide/submitting-guide.component.css +0 -0
- src/app/components/body/submitting/submitting-guide/submitting-guide.component.html +19 -0
- src/app/components/body/submitting/submitting-guide/submitting-guide.component.spec.ts +23 -0
- src/app/components/body/submitting/submitting-guide/submitting-guide.component.ts +12 -0
- src/app/components/body/submitting/submitting.component.html +4 -1
- src/app/components/body/submitting/submitting.component.ts +29 -18
- src/app/error_handling/global-error-handler.service.ts +81 -0
- src/app/error_handling/not-found/not-found.component.css +25 -0
- src/app/error_handling/not-found/not-found.component.html +5 -0
- src/app/error_handling/not-found/not-found.component.spec.ts +23 -0
- src/app/error_handling/not-found/not-found.component.ts +15 -0
- src/app/state_management/models/leaderboard-entry.model.ts +4 -0
- src/app/state_management/models/submission-entry.model.ts +1 -0
- src/app/state_management/models/task.mode.ts +0 -2
- src/app/state_management/services/app-state.service.ts +150 -129
- src/app/state_management/services/authentication.service.ts +6 -10
.DS_Store
CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
|
|
src/.DS_Store
CHANGED
Binary files a/src/.DS_Store and b/src/.DS_Store differ
|
|
src/app/.DS_Store
CHANGED
Binary files a/src/app/.DS_Store and b/src/app/.DS_Store differ
|
|
src/app/app.config.ts
CHANGED
@@ -1,15 +1,17 @@
|
|
1 |
-
import {
|
2 |
import {provideRouter, withComponentInputBinding} from '@angular/router';
|
3 |
|
4 |
import { routes } from './app.routes';
|
5 |
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
6 |
import {provideHttpClient, withFetch} from "@angular/common/http";
|
|
|
7 |
|
8 |
export const appConfig: ApplicationConfig = {
|
9 |
providers: [
|
10 |
provideRouter(routes, withComponentInputBinding()),
|
11 |
provideAnimationsAsync(),
|
12 |
provideAnimationsAsync(),
|
13 |
-
provideHttpClient(withFetch())
|
|
|
14 |
]
|
15 |
};
|
|
|
1 |
+
import {ApplicationConfig, ErrorHandler} from '@angular/core';
|
2 |
import {provideRouter, withComponentInputBinding} from '@angular/router';
|
3 |
|
4 |
import { routes } from './app.routes';
|
5 |
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
6 |
import {provideHttpClient, withFetch} from "@angular/common/http";
|
7 |
+
import {GlobalErrorHandlerService} from "./error_handling/global-error-handler.service";
|
8 |
|
9 |
export const appConfig: ApplicationConfig = {
|
10 |
providers: [
|
11 |
provideRouter(routes, withComponentInputBinding()),
|
12 |
provideAnimationsAsync(),
|
13 |
provideAnimationsAsync(),
|
14 |
+
provideHttpClient(withFetch()),
|
15 |
+
{provide: ErrorHandler, useClass: GlobalErrorHandlerService}
|
16 |
]
|
17 |
};
|
src/app/app.routes.ts
CHANGED
@@ -9,6 +9,7 @@ import {TaskComponent} from "./components/body/tasks/task/task.component";
|
|
9 |
import {SubmittingComponent} from "./components/body/submitting/submitting.component";
|
10 |
import {ControlPanelComponent} from "./components/body/control-panel/control-panel.component";
|
11 |
import {AdminLoginComponent} from "./components/body/control-panel/admin-login/admin-login.component";
|
|
|
12 |
|
13 |
export const routes: Routes = [
|
14 |
{path: '', component : BodyComponent},
|
@@ -20,5 +21,8 @@ export const routes: Routes = [
|
|
20 |
{path: 'about', component: AboutComponent},
|
21 |
{path: 'leaderboards', component: LeaderboardsComponent},
|
22 |
{path: 'leaderboards/:task', component: LeaderboardsComponent},
|
23 |
-
{path: 'control', component:
|
|
|
|
|
|
|
24 |
];
|
|
|
9 |
import {SubmittingComponent} from "./components/body/submitting/submitting.component";
|
10 |
import {ControlPanelComponent} from "./components/body/control-panel/control-panel.component";
|
11 |
import {AdminLoginComponent} from "./components/body/control-panel/admin-login/admin-login.component";
|
12 |
+
import {NotFoundComponent} from "./error_handling/not-found/not-found.component";
|
13 |
|
14 |
export const routes: Routes = [
|
15 |
{path: '', component : BodyComponent},
|
|
|
21 |
{path: 'about', component: AboutComponent},
|
22 |
{path: 'leaderboards', component: LeaderboardsComponent},
|
23 |
{path: 'leaderboards/:task', component: LeaderboardsComponent},
|
24 |
+
{path: 'control', component: AdminLoginComponent},
|
25 |
+
{path: 'control-panel', component: ControlPanelComponent},
|
26 |
+
{path: '404', component: NotFoundComponent},
|
27 |
+
{path: '**', redirectTo: '/404'}
|
28 |
];
|
src/app/components/.DS_Store
CHANGED
Binary files a/src/app/components/.DS_Store and b/src/app/components/.DS_Store differ
|
|
src/app/components/body/.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
src/app/components/body/body.component.html
CHANGED
@@ -80,7 +80,7 @@
|
|
80 |
<button mat-button routerLink="/submissions">Submissions List</button>
|
81 |
</nav>
|
82 |
|
83 |
-
<button mat-raised-button color="accent" class="submit-button">Submit a Model</button>
|
84 |
|
85 |
</mat-card-content>
|
86 |
</mat-card>
|
|
|
80 |
<button mat-button routerLink="/submissions">Submissions List</button>
|
81 |
</nav>
|
82 |
|
83 |
+
<button mat-raised-button routerLink="/submitting" color="accent" class="submit-button">Submit a Model</button>
|
84 |
|
85 |
</mat-card-content>
|
86 |
</mat-card>
|
src/app/components/body/control-panel/admin-login/admin-login.component.ts
CHANGED
@@ -7,6 +7,7 @@ import {MatInput} from "@angular/material/input";
|
|
7 |
import {AppStateService} from "../../../../state_management/services/app-state.service";
|
8 |
import {map} from "rxjs";
|
9 |
import {AsyncPipe, NgIf} from "@angular/common";
|
|
|
10 |
|
11 |
@Component({
|
12 |
selector: 'app-admin-login',
|
@@ -19,7 +20,8 @@ import {AsyncPipe, NgIf} from "@angular/common";
|
|
19 |
MatInput,
|
20 |
MatLabel,
|
21 |
NgIf,
|
22 |
-
AsyncPipe
|
|
|
23 |
],
|
24 |
templateUrl: './admin-login.component.html',
|
25 |
styleUrl: './admin-login.component.css'
|
@@ -27,13 +29,20 @@ import {AsyncPipe, NgIf} from "@angular/common";
|
|
27 |
export class AdminLoginComponent {
|
28 |
password: string = '';
|
29 |
|
30 |
-
constructor(private stateService: AppStateService
|
|
|
|
|
31 |
|
32 |
authenticationState = this.stateService.state$.pipe(
|
33 |
map(state => state.adminSessionStatus)
|
34 |
);
|
35 |
|
36 |
onSubmit() {
|
37 |
-
this.stateService.authenticate(this.password)
|
|
|
|
|
|
|
|
|
|
|
38 |
}
|
39 |
}
|
|
|
7 |
import {AppStateService} from "../../../../state_management/services/app-state.service";
|
8 |
import {map} from "rxjs";
|
9 |
import {AsyncPipe, NgIf} from "@angular/common";
|
10 |
+
import {Router, RouterModule} from "@angular/router";
|
11 |
|
12 |
@Component({
|
13 |
selector: 'app-admin-login',
|
|
|
20 |
MatInput,
|
21 |
MatLabel,
|
22 |
NgIf,
|
23 |
+
AsyncPipe,
|
24 |
+
RouterModule
|
25 |
],
|
26 |
templateUrl: './admin-login.component.html',
|
27 |
styleUrl: './admin-login.component.css'
|
|
|
29 |
export class AdminLoginComponent {
|
30 |
password: string = '';
|
31 |
|
32 |
+
constructor(private stateService: AppStateService,
|
33 |
+
private router: Router
|
34 |
+
) {}
|
35 |
|
36 |
authenticationState = this.stateService.state$.pipe(
|
37 |
map(state => state.adminSessionStatus)
|
38 |
);
|
39 |
|
40 |
onSubmit() {
|
41 |
+
this.stateService.authenticate(this.password).subscribe(
|
42 |
+
next => {
|
43 |
+
console.log('Authentication successful');
|
44 |
+
this.router.navigate(['/control-panel']);
|
45 |
+
}
|
46 |
+
);
|
47 |
}
|
48 |
}
|
src/app/components/body/control-panel/control-panel.component.html
CHANGED
@@ -1,5 +1,3 @@
|
|
1 |
-
<app-admin-login *ngIf="(authenticationState | async) != 'authenticated'"></app-admin-login>
|
2 |
-
|
3 |
<div *ngIf="(authenticationState | async) == 'authenticated'">
|
4 |
<!-- Admin Control Panel -->
|
5 |
<mat-card>
|
|
|
|
|
|
|
1 |
<div *ngIf="(authenticationState | async) == 'authenticated'">
|
2 |
<!-- Admin Control Panel -->
|
3 |
<mat-card>
|
src/app/components/body/control-panel/control-panel.component.ts
CHANGED
@@ -78,7 +78,7 @@ export class ControlPanelComponent implements OnInit {
|
|
78 |
}
|
79 |
|
80 |
refresh() {
|
81 |
-
this.stateService.
|
82 |
}
|
83 |
|
84 |
editRow(entry: ControlPanelEntry) {
|
@@ -90,7 +90,7 @@ export class ControlPanelComponent implements OnInit {
|
|
90 |
// Modify the entry in the local data source
|
91 |
const index = this.dataSource.data.findIndex(e => e.id === entry.id);
|
92 |
this.dataSource.data[index] = entry;
|
93 |
-
this.stateService.
|
94 |
}
|
95 |
|
96 |
deleteRow(id: number) {
|
|
|
78 |
}
|
79 |
|
80 |
refresh() {
|
81 |
+
this.stateService.refreshControlPanel();
|
82 |
}
|
83 |
|
84 |
editRow(entry: ControlPanelEntry) {
|
|
|
90 |
// Modify the entry in the local data source
|
91 |
const index = this.dataSource.data.findIndex(e => e.id === entry.id);
|
92 |
this.dataSource.data[index] = entry;
|
93 |
+
this.stateService.updateSubmission(entry);
|
94 |
}
|
95 |
|
96 |
deleteRow(id: number) {
|
src/app/components/body/leaderboards/leaderboard/leaderboard.component.html
CHANGED
@@ -1,84 +1,66 @@
|
|
1 |
<mat-card>
|
2 |
<button mat-raised-button color="primary" (click)="refresh()">Refresh Leaderboard</button>
|
3 |
-
<table mat-table [dataSource]=dataSource>
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
<ng-container matColumnDef="model">
|
6 |
-
<mat-header-cell
|
7 |
-
*matHeaderCellDef
|
8 |
-
>
|
9 |
-
Model
|
10 |
-
</mat-header-cell>
|
11 |
<mat-cell *matCellDef="let entry">
|
12 |
<a *ngIf="entry.is_public" [href]="entry.link" target="_blank"> {{ entry.model }}</a>
|
13 |
<span *ngIf="entry.isPublic">{{ entry.model }}</span>
|
14 |
</mat-cell>
|
15 |
</ng-container>
|
16 |
|
|
|
17 |
<ng-container matColumnDef="team">
|
18 |
-
<mat-header-cell
|
19 |
-
*matHeaderCellDef
|
20 |
-
>
|
21 |
-
Team
|
22 |
-
</mat-header-cell>
|
23 |
<mat-cell *matCellDef="let entry">
|
24 |
<a *ngIf="entry.is_public" [href]="'mailto:' + entry.email" target="_blank"> {{ entry.team }}</a>
|
25 |
<span *ngIf="!entry.is_public">{{ entry.team }}</span>
|
26 |
</mat-cell>
|
27 |
</ng-container>
|
28 |
|
|
|
29 |
<ng-container matColumnDef="predictions">
|
30 |
-
<mat-header-cell
|
31 |
-
*matHeaderCellDef
|
32 |
-
>
|
33 |
-
Predictions
|
34 |
-
</mat-header-cell>
|
35 |
<mat-cell *matCellDef="let entry">
|
36 |
-
<a *ngIf="entry.is_public" [href]="
|
37 |
<span *ngIf="!entry.is_public">Private</span>
|
38 |
</mat-cell>
|
39 |
</ng-container>
|
40 |
|
|
|
41 |
<ng-container matColumnDef="accuracy">
|
42 |
-
<mat-header-cell
|
43 |
-
*matHeaderCellDef
|
44 |
-
>
|
45 |
-
Accuracy
|
46 |
-
</mat-header-cell>
|
47 |
<mat-cell *matCellDef="let entry">{{ entry.accuracy | number: '1.2-2' }}</mat-cell>
|
48 |
</ng-container>
|
49 |
|
|
|
50 |
<ng-container matColumnDef="precision">
|
51 |
-
<mat-header-cell
|
52 |
-
*matHeaderCellDef
|
53 |
-
>
|
54 |
-
Precision
|
55 |
-
</mat-header-cell>
|
56 |
<mat-cell *matCellDef="let entry">{{ entry.precision | number: '1.2-2' }}</mat-cell>
|
57 |
</ng-container>
|
58 |
|
|
|
59 |
<ng-container matColumnDef="recall">
|
60 |
-
<mat-header-cell
|
61 |
-
*matHeaderCellDef
|
62 |
-
>
|
63 |
-
Recall
|
64 |
-
</mat-header-cell>
|
65 |
<mat-cell *matCellDef="let entry">{{ entry.recall | number: '1.2-2'}}</mat-cell>
|
66 |
</ng-container>
|
67 |
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
>
|
72 |
-
F1
|
73 |
-
</mat-header-cell>
|
74 |
<mat-cell *matCellDef="let entry">{{ entry.f1_score | number: '1.2-2' }}</mat-cell>
|
75 |
</ng-container>
|
76 |
|
77 |
-
|
78 |
-
|
79 |
-
<mat-header-row
|
80 |
-
*matHeaderRowDef="displayedColumns; sticky: true"
|
81 |
-
></mat-header-row>
|
82 |
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
83 |
</table>
|
84 |
</mat-card>
|
|
|
1 |
<mat-card>
|
2 |
<button mat-raised-button color="primary" (click)="refresh()">Refresh Leaderboard</button>
|
3 |
+
<table mat-table [dataSource]="dataSource" matSort>
|
4 |
|
5 |
+
<!-- Index Column -->
|
6 |
+
<ng-container matColumnDef="index">
|
7 |
+
<mat-header-cell *matHeaderCellDef> # </mat-header-cell>
|
8 |
+
<mat-cell *matCellDef="let entry; let i = index"> {{i + 1}} </mat-cell>
|
9 |
+
</ng-container>
|
10 |
+
|
11 |
+
<!-- Model Column -->
|
12 |
<ng-container matColumnDef="model">
|
13 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Model </mat-header-cell>
|
|
|
|
|
|
|
|
|
14 |
<mat-cell *matCellDef="let entry">
|
15 |
<a *ngIf="entry.is_public" [href]="entry.link" target="_blank"> {{ entry.model }}</a>
|
16 |
<span *ngIf="entry.isPublic">{{ entry.model }}</span>
|
17 |
</mat-cell>
|
18 |
</ng-container>
|
19 |
|
20 |
+
<!-- Team Column -->
|
21 |
<ng-container matColumnDef="team">
|
22 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Team </mat-header-cell>
|
|
|
|
|
|
|
|
|
23 |
<mat-cell *matCellDef="let entry">
|
24 |
<a *ngIf="entry.is_public" [href]="'mailto:' + entry.email" target="_blank"> {{ entry.team }}</a>
|
25 |
<span *ngIf="!entry.is_public">{{ entry.team }}</span>
|
26 |
</mat-cell>
|
27 |
</ng-container>
|
28 |
|
29 |
+
<!-- Predictions Column -->
|
30 |
<ng-container matColumnDef="predictions">
|
31 |
+
<mat-header-cell *matHeaderCellDef> Predictions </mat-header-cell>
|
|
|
|
|
|
|
|
|
32 |
<mat-cell *matCellDef="let entry">
|
33 |
+
<a *ngIf="entry.is_public" [href]="entry.blob_url" target="_blank" download="{{entry.model + '-predictions.txt'}}">Download</a>
|
34 |
<span *ngIf="!entry.is_public">Private</span>
|
35 |
</mat-cell>
|
36 |
</ng-container>
|
37 |
|
38 |
+
<!-- Accuracy Column -->
|
39 |
<ng-container matColumnDef="accuracy">
|
40 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Accuracy </mat-header-cell>
|
|
|
|
|
|
|
|
|
41 |
<mat-cell *matCellDef="let entry">{{ entry.accuracy | number: '1.2-2' }}</mat-cell>
|
42 |
</ng-container>
|
43 |
|
44 |
+
<!-- Precision Column -->
|
45 |
<ng-container matColumnDef="precision">
|
46 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Precision </mat-header-cell>
|
|
|
|
|
|
|
|
|
47 |
<mat-cell *matCellDef="let entry">{{ entry.precision | number: '1.2-2' }}</mat-cell>
|
48 |
</ng-container>
|
49 |
|
50 |
+
<!-- Recall Column -->
|
51 |
<ng-container matColumnDef="recall">
|
52 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Recall </mat-header-cell>
|
|
|
|
|
|
|
|
|
53 |
<mat-cell *matCellDef="let entry">{{ entry.recall | number: '1.2-2'}}</mat-cell>
|
54 |
</ng-container>
|
55 |
|
56 |
+
<!-- F1 Column -->
|
57 |
+
<ng-container matColumnDef="f1_score">
|
58 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> F1 </mat-header-cell>
|
|
|
|
|
|
|
59 |
<mat-cell *matCellDef="let entry">{{ entry.f1_score | number: '1.2-2' }}</mat-cell>
|
60 |
</ng-container>
|
61 |
|
62 |
+
<!-- Header and Row Declarations -->
|
63 |
+
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
|
|
|
|
|
|
|
64 |
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
65 |
</table>
|
66 |
</mat-card>
|
src/app/components/body/leaderboards/leaderboard/leaderboard.component.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import {Component, Input, OnInit} from '@angular/core';
|
2 |
import {MatCard} from "@angular/material/card";
|
3 |
import {
|
4 |
MatCell,
|
@@ -13,6 +13,7 @@ import {MatButton} from "@angular/material/button";
|
|
13 |
import {AppStateService} from "../../../../state_management/services/app-state.service";
|
14 |
import {map} from "rxjs";
|
15 |
import {DecimalPipe, NgIf} from "@angular/common";
|
|
|
16 |
|
17 |
@Component({
|
18 |
selector: 'app-leaderboard',
|
@@ -31,21 +32,24 @@ import {DecimalPipe, NgIf} from "@angular/common";
|
|
31 |
MatRowDef,
|
32 |
MatButton,
|
33 |
NgIf,
|
34 |
-
DecimalPipe
|
|
|
|
|
35 |
],
|
36 |
templateUrl: './leaderboard.component.html',
|
37 |
styleUrl: './leaderboard.component.css'
|
38 |
})
|
39 |
-
export class LeaderboardComponent implements OnInit {
|
40 |
|
41 |
displayedColumns: string[] = [
|
|
|
42 |
'model',
|
43 |
'team',
|
44 |
'predictions',
|
45 |
'accuracy',
|
46 |
'precision',
|
47 |
'recall',
|
48 |
-
'
|
49 |
];
|
50 |
|
51 |
@Input()
|
@@ -57,6 +61,12 @@ export class LeaderboardComponent implements OnInit {
|
|
57 |
dataSource = new MatTableDataSource<LeaderboardEntry>();
|
58 |
leaderboards = this.stateService.state$.pipe(map(state => state.leaderboards));
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
|
61 |
constructor(private stateService: AppStateService) {
|
62 |
|
@@ -65,14 +75,15 @@ export class LeaderboardComponent implements OnInit {
|
|
65 |
ngOnInit() {
|
66 |
this.leaderboards.subscribe(
|
67 |
data => {
|
68 |
-
// choose only entries where task == this.task and dataset == this.dataset.
|
69 |
-
// assign the data to the dataSource
|
70 |
this.dataSource.data = data.filter(
|
71 |
entry => (entry.task == this.task && entry.dataset == this.dataset)
|
72 |
-
)
|
|
|
|
|
|
|
73 |
}
|
74 |
);
|
75 |
-
this.
|
76 |
}
|
77 |
|
78 |
getTextDownloadURL(prediction: string) {
|
@@ -80,6 +91,6 @@ export class LeaderboardComponent implements OnInit {
|
|
80 |
}
|
81 |
|
82 |
refresh() {
|
83 |
-
this.stateService.
|
84 |
}
|
85 |
}
|
|
|
1 |
+
import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
|
2 |
import {MatCard} from "@angular/material/card";
|
3 |
import {
|
4 |
MatCell,
|
|
|
13 |
import {AppStateService} from "../../../../state_management/services/app-state.service";
|
14 |
import {map} from "rxjs";
|
15 |
import {DecimalPipe, NgIf} from "@angular/common";
|
16 |
+
import {MatSort, MatSortModule} from "@angular/material/sort";
|
17 |
|
18 |
@Component({
|
19 |
selector: 'app-leaderboard',
|
|
|
32 |
MatRowDef,
|
33 |
MatButton,
|
34 |
NgIf,
|
35 |
+
DecimalPipe,
|
36 |
+
MatSort,
|
37 |
+
MatSortModule
|
38 |
],
|
39 |
templateUrl: './leaderboard.component.html',
|
40 |
styleUrl: './leaderboard.component.css'
|
41 |
})
|
42 |
+
export class LeaderboardComponent implements OnInit, AfterViewInit {
|
43 |
|
44 |
displayedColumns: string[] = [
|
45 |
+
'index',
|
46 |
'model',
|
47 |
'team',
|
48 |
'predictions',
|
49 |
'accuracy',
|
50 |
'precision',
|
51 |
'recall',
|
52 |
+
'f1_score'
|
53 |
];
|
54 |
|
55 |
@Input()
|
|
|
61 |
dataSource = new MatTableDataSource<LeaderboardEntry>();
|
62 |
leaderboards = this.stateService.state$.pipe(map(state => state.leaderboards));
|
63 |
|
64 |
+
@ViewChild(MatSort) sort: MatSort | undefined;
|
65 |
+
|
66 |
+
ngAfterViewInit() {
|
67 |
+
if (this.sort)
|
68 |
+
this.dataSource.sort = this.sort;
|
69 |
+
}
|
70 |
|
71 |
constructor(private stateService: AppStateService) {
|
72 |
|
|
|
75 |
ngOnInit() {
|
76 |
this.leaderboards.subscribe(
|
77 |
data => {
|
|
|
|
|
78 |
this.dataSource.data = data.filter(
|
79 |
entry => (entry.task == this.task && entry.dataset == this.dataset)
|
80 |
+
).map(entry => {
|
81 |
+
entry.blob_url = this.getTextDownloadURL(entry.predictions); // Precompute Blob URL
|
82 |
+
return entry;
|
83 |
+
});
|
84 |
}
|
85 |
);
|
86 |
+
this.refresh();
|
87 |
}
|
88 |
|
89 |
getTextDownloadURL(prediction: string) {
|
|
|
91 |
}
|
92 |
|
93 |
refresh() {
|
94 |
+
this.stateService.refreshLeaderboards();
|
95 |
}
|
96 |
}
|
src/app/components/body/submissions/submissions.component.html
CHANGED
@@ -1,90 +1,71 @@
|
|
1 |
-
<!---
|
2 |
-
This component is used to display a list of submissions for all users, and allows a new submission to be made.
|
3 |
-
-->
|
4 |
-
|
5 |
<mat-card>
|
6 |
<mat-card-content>
|
7 |
<button mat-raised-button color="primary" style="width: 100%" (click)="refresh()">Refresh Submissions</button>
|
8 |
</mat-card-content>
|
9 |
|
10 |
-
<table mat-table [dataSource]=dataSource>
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
|
|
12 |
<ng-container matColumnDef="team">
|
13 |
-
<mat-header-cell
|
14 |
-
*matHeaderCellDef
|
15 |
-
>
|
16 |
-
Team
|
17 |
-
</mat-header-cell>
|
18 |
<mat-cell *matCellDef="let entry">
|
19 |
<a *ngIf="entry.is_public" [href]="'mailto:' + entry.email" target="_blank"> {{ entry.team }}</a>
|
20 |
<span *ngIf="!entry.is_public">{{ entry.team }}</span>
|
21 |
</mat-cell>
|
22 |
</ng-container>
|
23 |
|
|
|
24 |
<ng-container matColumnDef="task">
|
25 |
-
<mat-header-cell
|
26 |
-
*matHeaderCellDef
|
27 |
-
>
|
28 |
-
Task
|
29 |
-
</mat-header-cell>
|
30 |
<mat-cell *matCellDef="let entry">{{ entry.task }}</mat-cell>
|
31 |
</ng-container>
|
32 |
|
|
|
33 |
<ng-container matColumnDef="dataset">
|
34 |
-
<mat-header-cell
|
35 |
-
*matHeaderCellDef
|
36 |
-
>
|
37 |
-
Dataset
|
38 |
-
</mat-header-cell>
|
39 |
<mat-cell *matCellDef="let entry">{{ entry.dataset }}</mat-cell>
|
40 |
</ng-container>
|
41 |
|
|
|
42 |
<ng-container matColumnDef="model">
|
43 |
-
<mat-header-cell
|
44 |
-
*matHeaderCellDef
|
45 |
-
>
|
46 |
-
Model
|
47 |
-
</mat-header-cell>
|
48 |
<mat-cell *matCellDef="let entry">
|
49 |
<a *ngIf="entry.is_public" [href]="entry.link" target="_blank"> {{ entry.model }}</a>
|
50 |
<span *ngIf="entry.isPublic">{{ entry.model }}</span>
|
51 |
</mat-cell>
|
52 |
</ng-container>
|
53 |
|
|
|
54 |
<ng-container matColumnDef="predictions">
|
55 |
-
<mat-header-cell
|
56 |
-
*matHeaderCellDef
|
57 |
-
>
|
58 |
-
Predictions
|
59 |
-
</mat-header-cell>
|
60 |
<mat-cell *matCellDef="let entry">
|
61 |
-
<a *ngIf="entry.is_public" [href]="
|
|
|
62 |
<span *ngIf="!entry.is_public">Private</span>
|
63 |
</mat-cell>
|
64 |
</ng-container>
|
65 |
|
|
|
66 |
<ng-container matColumnDef="status">
|
67 |
-
<mat-header-cell
|
68 |
-
*matHeaderCellDef
|
69 |
-
>
|
70 |
-
Status
|
71 |
-
</mat-header-cell>
|
72 |
<mat-cell *matCellDef="let entry">{{ entry.status }}</mat-cell>
|
73 |
</ng-container>
|
74 |
|
|
|
75 |
<ng-container matColumnDef="time">
|
76 |
-
<mat-header-cell
|
77 |
-
*matHeaderCellDef
|
78 |
-
>
|
79 |
-
Time
|
80 |
-
</mat-header-cell>
|
81 |
<mat-cell *matCellDef="let entry">{{ entry.time }}</mat-cell>
|
82 |
</ng-container>
|
83 |
|
84 |
-
|
85 |
-
<mat-header-row
|
86 |
-
*matHeaderRowDef="displayedColumns; sticky: true"
|
87 |
-
></mat-header-row>
|
88 |
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
|
|
89 |
</table>
|
90 |
</mat-card>
|
|
|
|
|
|
|
|
|
|
|
1 |
<mat-card>
|
2 |
<mat-card-content>
|
3 |
<button mat-raised-button color="primary" style="width: 100%" (click)="refresh()">Refresh Submissions</button>
|
4 |
</mat-card-content>
|
5 |
|
6 |
+
<table mat-table [dataSource]="dataSource" matSort>
|
7 |
+
|
8 |
+
<!-- Index Column -->
|
9 |
+
<ng-container matColumnDef="index">
|
10 |
+
<mat-header-cell *matHeaderCellDef> #</mat-header-cell>
|
11 |
+
<mat-cell *matCellDef="let entry; let i = index"> {{ i + 1 }}</mat-cell>
|
12 |
+
</ng-container>
|
13 |
|
14 |
+
<!-- Team Column -->
|
15 |
<ng-container matColumnDef="team">
|
16 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Team</mat-header-cell>
|
|
|
|
|
|
|
|
|
17 |
<mat-cell *matCellDef="let entry">
|
18 |
<a *ngIf="entry.is_public" [href]="'mailto:' + entry.email" target="_blank"> {{ entry.team }}</a>
|
19 |
<span *ngIf="!entry.is_public">{{ entry.team }}</span>
|
20 |
</mat-cell>
|
21 |
</ng-container>
|
22 |
|
23 |
+
<!-- Task Column -->
|
24 |
<ng-container matColumnDef="task">
|
25 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Task</mat-header-cell>
|
|
|
|
|
|
|
|
|
26 |
<mat-cell *matCellDef="let entry">{{ entry.task }}</mat-cell>
|
27 |
</ng-container>
|
28 |
|
29 |
+
<!-- Dataset Column -->
|
30 |
<ng-container matColumnDef="dataset">
|
31 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Dataset</mat-header-cell>
|
|
|
|
|
|
|
|
|
32 |
<mat-cell *matCellDef="let entry">{{ entry.dataset }}</mat-cell>
|
33 |
</ng-container>
|
34 |
|
35 |
+
<!-- Model Column -->
|
36 |
<ng-container matColumnDef="model">
|
37 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Model</mat-header-cell>
|
|
|
|
|
|
|
|
|
38 |
<mat-cell *matCellDef="let entry">
|
39 |
<a *ngIf="entry.is_public" [href]="entry.link" target="_blank"> {{ entry.model }}</a>
|
40 |
<span *ngIf="entry.isPublic">{{ entry.model }}</span>
|
41 |
</mat-cell>
|
42 |
</ng-container>
|
43 |
|
44 |
+
<!-- Predictions Column -->
|
45 |
<ng-container matColumnDef="predictions">
|
46 |
+
<mat-header-cell *matHeaderCellDef> Predictions</mat-header-cell>
|
|
|
|
|
|
|
|
|
47 |
<mat-cell *matCellDef="let entry">
|
48 |
+
<a *ngIf="entry.is_public" [href]="entry.blob_url" target="_blank"
|
49 |
+
download="{{entry.model + '-predictions.txt'}}">Download</a>
|
50 |
<span *ngIf="!entry.is_public">Private</span>
|
51 |
</mat-cell>
|
52 |
</ng-container>
|
53 |
|
54 |
+
<!-- Status Column -->
|
55 |
<ng-container matColumnDef="status">
|
56 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Status</mat-header-cell>
|
|
|
|
|
|
|
|
|
57 |
<mat-cell *matCellDef="let entry">{{ entry.status }}</mat-cell>
|
58 |
</ng-container>
|
59 |
|
60 |
+
<!-- Time Column -->
|
61 |
<ng-container matColumnDef="time">
|
62 |
+
<mat-header-cell *matHeaderCellDef mat-sort-header> Time</mat-header-cell>
|
|
|
|
|
|
|
|
|
63 |
<mat-cell *matCellDef="let entry">{{ entry.time }}</mat-cell>
|
64 |
</ng-container>
|
65 |
|
66 |
+
<!-- Header and Row Declarations -->
|
67 |
+
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
|
|
|
|
|
68 |
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
69 |
+
|
70 |
</table>
|
71 |
</mat-card>
|
src/app/components/body/submissions/submissions.component.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import {Component, Input, OnInit} from '@angular/core';
|
2 |
import {MatCard, MatCardActions, MatCardContent} from "@angular/material/card";
|
3 |
import {MatFormField, MatLabel} from "@angular/material/form-field";
|
4 |
import {MatInput} from "@angular/material/input";
|
@@ -21,6 +21,7 @@ import {
|
|
21 |
import {LeaderboardEntry} from "../../../state_management/models/leaderboard-entry.model";
|
22 |
import {SubmissionEntry} from "../../../state_management/models/submission-entry.model";
|
23 |
import {MatTab, MatTabGroup} from "@angular/material/tabs";
|
|
|
24 |
|
25 |
@Component({
|
26 |
selector: 'app-submissions',
|
@@ -48,14 +49,17 @@ import {MatTab, MatTabGroup} from "@angular/material/tabs";
|
|
48 |
MatTable,
|
49 |
MatHeaderCellDef,
|
50 |
MatTabGroup,
|
51 |
-
MatTab
|
|
|
|
|
52 |
],
|
53 |
templateUrl: './submissions.component.html',
|
54 |
styleUrl: './submissions.component.css'
|
55 |
})
|
56 |
-
export class SubmissionsComponent implements OnInit {
|
57 |
|
58 |
displayedColumns: string[] = [
|
|
|
59 |
'team',
|
60 |
'model',
|
61 |
'task',
|
@@ -65,6 +69,12 @@ export class SubmissionsComponent implements OnInit {
|
|
65 |
'time',
|
66 |
];
|
67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
@Input()
|
69 |
task: string = '';
|
70 |
|
@@ -84,9 +94,13 @@ export class SubmissionsComponent implements OnInit {
|
|
84 |
ngOnInit() {
|
85 |
this.submissions.subscribe(
|
86 |
data => {
|
87 |
-
this.dataSource.data = data
|
|
|
|
|
|
|
88 |
}
|
89 |
)
|
|
|
90 |
}
|
91 |
|
92 |
getTextDownloadURL(prediction: string) {
|
@@ -94,6 +108,6 @@ export class SubmissionsComponent implements OnInit {
|
|
94 |
}
|
95 |
|
96 |
refresh() {
|
97 |
-
this.state.
|
98 |
}
|
99 |
}
|
|
|
1 |
+
import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
|
2 |
import {MatCard, MatCardActions, MatCardContent} from "@angular/material/card";
|
3 |
import {MatFormField, MatLabel} from "@angular/material/form-field";
|
4 |
import {MatInput} from "@angular/material/input";
|
|
|
21 |
import {LeaderboardEntry} from "../../../state_management/models/leaderboard-entry.model";
|
22 |
import {SubmissionEntry} from "../../../state_management/models/submission-entry.model";
|
23 |
import {MatTab, MatTabGroup} from "@angular/material/tabs";
|
24 |
+
import {MatSort, MatSortModule} from "@angular/material/sort";
|
25 |
|
26 |
@Component({
|
27 |
selector: 'app-submissions',
|
|
|
49 |
MatTable,
|
50 |
MatHeaderCellDef,
|
51 |
MatTabGroup,
|
52 |
+
MatTab,
|
53 |
+
MatSort,
|
54 |
+
MatSortModule
|
55 |
],
|
56 |
templateUrl: './submissions.component.html',
|
57 |
styleUrl: './submissions.component.css'
|
58 |
})
|
59 |
+
export class SubmissionsComponent implements OnInit, AfterViewInit {
|
60 |
|
61 |
displayedColumns: string[] = [
|
62 |
+
'index',
|
63 |
'team',
|
64 |
'model',
|
65 |
'task',
|
|
|
69 |
'time',
|
70 |
];
|
71 |
|
72 |
+
@ViewChild(MatSort) sort: MatSort | undefined;
|
73 |
+
|
74 |
+
ngAfterViewInit() {
|
75 |
+
if (this.sort)
|
76 |
+
this.dataSource.sort = this.sort;
|
77 |
+
}
|
78 |
@Input()
|
79 |
task: string = '';
|
80 |
|
|
|
94 |
ngOnInit() {
|
95 |
this.submissions.subscribe(
|
96 |
data => {
|
97 |
+
this.dataSource.data = data.map(entry => {
|
98 |
+
entry.blob_url = this.getTextDownloadURL(entry.predictions); // Precompute Blob URL
|
99 |
+
return entry;
|
100 |
+
})
|
101 |
}
|
102 |
)
|
103 |
+
this.state.refreshSubmissions();
|
104 |
}
|
105 |
|
106 |
getTextDownloadURL(prediction: string) {
|
|
|
108 |
}
|
109 |
|
110 |
refresh() {
|
111 |
+
this.state.refreshSubmissions();
|
112 |
}
|
113 |
}
|
src/app/components/body/submitting/submitting-guide/submitting-guide.component.css
ADDED
File without changes
|
src/app/components/body/submitting/submitting-guide/submitting-guide.component.html
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div style="margin: 10px 0; padding: 10px; width: 100%;">
|
2 |
+
<h2 style="color: #424242; font-weight: 500; font-size: 1.5rem;">How to Submit a Model</h2>
|
3 |
+
<p style="line-height: 1.6; color: #616161; font-size: 1rem;">
|
4 |
+
Please fill out the form above with the required details. The following fields are optional but strongly recommended:
|
5 |
+
</p>
|
6 |
+
<ul style="line-height: 1.6; color: #424242; font-size: 1rem; padding-left: 20px;">
|
7 |
+
<li><strong>Team Name</strong></li>
|
8 |
+
<li><strong>Contact Email</strong></li>
|
9 |
+
<li><strong>Model Link</strong>
|
10 |
+
<span style="font-weight: 400;">(This could be a download link, a Hugging Face model hub link, a GitHub link, or any other link that provides access to your model.)</span>
|
11 |
+
</li>
|
12 |
+
</ul>
|
13 |
+
<p style="line-height: 1.6; color: #616161; font-size: 1rem;">
|
14 |
+
Ensure the uploaded predictions file is a CSV with a column named <strong>"predictions"</strong>, containing the model's predictions in the same order as the test data.
|
15 |
+
</p>
|
16 |
+
<p style="line-height: 1.6; color: #616161; font-size: 1rem;">
|
17 |
+
If you opt to make your predictions private, the model link will also be kept private.
|
18 |
+
</p>
|
19 |
+
</div>
|
src/app/components/body/submitting/submitting-guide/submitting-guide.component.spec.ts
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { SubmittingGuideComponent } from './submitting-guide.component';
|
4 |
+
|
5 |
+
describe('SubmittingGuideComponent', () => {
|
6 |
+
let component: SubmittingGuideComponent;
|
7 |
+
let fixture: ComponentFixture<SubmittingGuideComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
imports: [SubmittingGuideComponent]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(SubmittingGuideComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
src/app/components/body/submitting/submitting-guide/submitting-guide.component.ts
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Component } from '@angular/core';
|
2 |
+
|
3 |
+
@Component({
|
4 |
+
selector: 'app-submitting-guide',
|
5 |
+
standalone: true,
|
6 |
+
imports: [],
|
7 |
+
templateUrl: './submitting-guide.component.html',
|
8 |
+
styleUrl: './submitting-guide.component.css'
|
9 |
+
})
|
10 |
+
export class SubmittingGuideComponent {
|
11 |
+
|
12 |
+
}
|
src/app/components/body/submitting/submitting.component.html
CHANGED
@@ -83,5 +83,8 @@
|
|
83 |
</mat-card-actions>
|
84 |
|
85 |
</form>
|
86 |
-
|
|
|
|
|
|
|
87 |
</mat-card>
|
|
|
83 |
</mat-card-actions>
|
84 |
|
85 |
</form>
|
86 |
+
</mat-card>
|
87 |
+
<br>
|
88 |
+
<mat-card>
|
89 |
+
<app-submitting-guide></app-submitting-guide>
|
90 |
</mat-card>
|
src/app/components/body/submitting/submitting.component.ts
CHANGED
@@ -11,6 +11,7 @@ import {AppStateService} from "../../../state_management/services/app-state.serv
|
|
11 |
import {map, Observer} from "rxjs";
|
12 |
import {FormGroup, FormBuilder, Validators} from "@angular/forms";
|
13 |
import {error} from "@angular/compiler-cli/src/transformers/util";
|
|
|
14 |
|
15 |
|
16 |
@Component({
|
@@ -30,12 +31,13 @@ import {error} from "@angular/compiler-cli/src/transformers/util";
|
|
30 |
ReactiveFormsModule,
|
31 |
FormsModule,
|
32 |
AsyncPipe,
|
33 |
-
MatError
|
|
|
34 |
],
|
35 |
templateUrl: './submitting.component.html',
|
36 |
styleUrl: './submitting.component.css'
|
37 |
})
|
38 |
-
export class SubmittingComponent implements OnInit{
|
39 |
|
40 |
tasks = this.stateService.state$.pipe(map(state => state.tasks));
|
41 |
datasets = this.stateService.state$.pipe(map(state => state.datasets));
|
@@ -63,17 +65,16 @@ export class SubmittingComponent implements OnInit{
|
|
63 |
}
|
64 |
|
65 |
|
66 |
-
ngOnInit(){
|
67 |
}
|
68 |
|
69 |
-
onSubmit
|
70 |
this.message = '';
|
71 |
|
72 |
if (this.form.invalid) {
|
73 |
this.message = 'Invalid form';
|
74 |
return;
|
75 |
}
|
76 |
-
|
77 |
this.message = 'Submitting...';
|
78 |
this.stateService.submit(
|
79 |
this.form.value.modelName,
|
@@ -86,28 +87,38 @@ export class SubmittingComponent implements OnInit{
|
|
86 |
this.fileContent
|
87 |
).subscribe(
|
88 |
{
|
89 |
-
next: (
|
90 |
-
|
|
|
91 |
},
|
92 |
-
error: (
|
93 |
-
|
94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
}
|
96 |
}
|
97 |
);
|
98 |
}
|
99 |
|
100 |
-
onFileSelected(event
|
101 |
const file: File = event.target.files[0];
|
102 |
if (file) {
|
103 |
this.chosenFileName = file.name;
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
}else {
|
111 |
this.chosenFileName = 'Invalid file';
|
112 |
}
|
113 |
}
|
|
|
11 |
import {map, Observer} from "rxjs";
|
12 |
import {FormGroup, FormBuilder, Validators} from "@angular/forms";
|
13 |
import {error} from "@angular/compiler-cli/src/transformers/util";
|
14 |
+
import {SubmittingGuideComponent} from "./submitting-guide/submitting-guide.component";
|
15 |
|
16 |
|
17 |
@Component({
|
|
|
31 |
ReactiveFormsModule,
|
32 |
FormsModule,
|
33 |
AsyncPipe,
|
34 |
+
MatError,
|
35 |
+
SubmittingGuideComponent
|
36 |
],
|
37 |
templateUrl: './submitting.component.html',
|
38 |
styleUrl: './submitting.component.css'
|
39 |
})
|
40 |
+
export class SubmittingComponent implements OnInit {
|
41 |
|
42 |
tasks = this.stateService.state$.pipe(map(state => state.tasks));
|
43 |
datasets = this.stateService.state$.pipe(map(state => state.datasets));
|
|
|
65 |
}
|
66 |
|
67 |
|
68 |
+
ngOnInit() {
|
69 |
}
|
70 |
|
71 |
+
onSubmit() {
|
72 |
this.message = '';
|
73 |
|
74 |
if (this.form.invalid) {
|
75 |
this.message = 'Invalid form';
|
76 |
return;
|
77 |
}
|
|
|
78 |
this.message = 'Submitting...';
|
79 |
this.stateService.submit(
|
80 |
this.form.value.modelName,
|
|
|
87 |
this.fileContent
|
88 |
).subscribe(
|
89 |
{
|
90 |
+
next: (response: any) => {
|
91 |
+
console.log(response);
|
92 |
+
this.message = response[0].message;
|
93 |
},
|
94 |
+
error: (error: any) => {
|
95 |
+
switch (error.status) {
|
96 |
+
case 400:
|
97 |
+
this.message = 'Submission rejected';
|
98 |
+
break;
|
99 |
+
case 500:
|
100 |
+
this.message = 'Internal server error';
|
101 |
+
break;
|
102 |
+
default:
|
103 |
+
this.message = 'An unexpected error occurred. Please try again later.';
|
104 |
+
break;
|
105 |
+
}
|
106 |
}
|
107 |
}
|
108 |
);
|
109 |
}
|
110 |
|
111 |
+
onFileSelected(event: any) {
|
112 |
const file: File = event.target.files[0];
|
113 |
if (file) {
|
114 |
this.chosenFileName = file.name;
|
115 |
+
// read file as text
|
116 |
+
const reader = new FileReader();
|
117 |
+
reader.onload = (e: any) => {
|
118 |
+
this.fileContent = e.target.result;
|
119 |
+
};
|
120 |
+
reader.readAsText(file);
|
121 |
+
} else {
|
122 |
this.chosenFileName = 'Invalid file';
|
123 |
}
|
124 |
}
|
src/app/error_handling/global-error-handler.service.ts
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Injectable, ErrorHandler, Injector } from '@angular/core';
|
2 |
+
import { HttpErrorResponse } from '@angular/common/http';
|
3 |
+
import { Router } from '@angular/router';
|
4 |
+
|
5 |
+
@Injectable({
|
6 |
+
providedIn: 'root',
|
7 |
+
})
|
8 |
+
export class GlobalErrorHandlerService implements ErrorHandler {
|
9 |
+
|
10 |
+
constructor(private injector: Injector) {}
|
11 |
+
|
12 |
+
handleError(error: any): void {
|
13 |
+
// Log the error to the console
|
14 |
+
console.error('An error occurred:', error);
|
15 |
+
|
16 |
+
// Differentiate between error types
|
17 |
+
if (error instanceof HttpErrorResponse) {
|
18 |
+
// Handle HTTP errors
|
19 |
+
this.handleHttpError(error);
|
20 |
+
} else if (error instanceof TypeError) {
|
21 |
+
// Handle client-side errors (TypeErrors)
|
22 |
+
this.handleClientError(error);
|
23 |
+
} else if (error instanceof Error) {
|
24 |
+
// Handle generic JavaScript errors
|
25 |
+
this.handleGenericError(error);
|
26 |
+
} else {
|
27 |
+
// Handle any other type of error
|
28 |
+
this.handleUnknownError(error);
|
29 |
+
}
|
30 |
+
|
31 |
+
// Always log the error, could be to a server or external logging service
|
32 |
+
this.logError(error);
|
33 |
+
}
|
34 |
+
|
35 |
+
private handleHttpError(error: HttpErrorResponse): void {
|
36 |
+
// Customize the message for different status codes
|
37 |
+
let errorMessage = 'An unexpected error occurred. Please try again later.';
|
38 |
+
switch (error.status) {
|
39 |
+
case 400:
|
40 |
+
errorMessage = 'Bad Request: Please check your input.';
|
41 |
+
break;
|
42 |
+
case 401:
|
43 |
+
errorMessage = 'Unauthorized: Please log in again.';
|
44 |
+
break;
|
45 |
+
case 404:
|
46 |
+
errorMessage = 'Not Found: The requested resource was not found.';
|
47 |
+
break;
|
48 |
+
case 500:
|
49 |
+
errorMessage = 'Internal Server Error: Please try again later.';
|
50 |
+
break;
|
51 |
+
case 503:
|
52 |
+
errorMessage = 'Service Unavailable: Please try again later.';
|
53 |
+
break;
|
54 |
+
default:
|
55 |
+
errorMessage = `Unexpected Error: ${error.message}`;
|
56 |
+
break;
|
57 |
+
}
|
58 |
+
// Optionally, display the error to the user
|
59 |
+
alert(errorMessage);
|
60 |
+
}
|
61 |
+
|
62 |
+
private handleClientError(error: TypeError): void {
|
63 |
+
// Handle client-side errors
|
64 |
+
alert('A client-side error occurred. Please try again.');
|
65 |
+
}
|
66 |
+
|
67 |
+
private handleGenericError(error: Error): void {
|
68 |
+
// Handle generic JavaScript errors
|
69 |
+
alert('An unexpected error occurred. Please try again later.');
|
70 |
+
}
|
71 |
+
|
72 |
+
private handleUnknownError(error: any): void {
|
73 |
+
// Handle unknown errors
|
74 |
+
alert('An unknown error occurred. Please try again later.');
|
75 |
+
}
|
76 |
+
|
77 |
+
private logError(error: any): void {
|
78 |
+
// Implement your logging logic here, e.g., send the error to a server
|
79 |
+
console.error('Logging the error:', error);
|
80 |
+
}
|
81 |
+
}
|
src/app/error_handling/not-found/not-found.component.css
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.not-found-container {
|
2 |
+
text-align: center;
|
3 |
+
margin-top: 50px;
|
4 |
+
}
|
5 |
+
|
6 |
+
.not-found-container h1 {
|
7 |
+
font-size: 3em;
|
8 |
+
margin-bottom: 20px;
|
9 |
+
}
|
10 |
+
|
11 |
+
.not-found-container p {
|
12 |
+
font-size: 1.5em;
|
13 |
+
margin-bottom: 30px;
|
14 |
+
}
|
15 |
+
|
16 |
+
.home-link {
|
17 |
+
font-size: 1.2em;
|
18 |
+
color: #007bff;
|
19 |
+
text-decoration: none;
|
20 |
+
transition: color 0.2s ease-in-out;
|
21 |
+
}
|
22 |
+
|
23 |
+
.home-link:hover {
|
24 |
+
color: #0056b3;
|
25 |
+
}
|
src/app/error_handling/not-found/not-found.component.html
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="not-found-container">
|
2 |
+
<h1>404 - Page Not Found</h1>
|
3 |
+
<p>Sorry, the page you are looking for does not exist.</p>
|
4 |
+
<a routerLink="/" class="home-link">Go back to Home</a>
|
5 |
+
</div>
|
src/app/error_handling/not-found/not-found.component.spec.ts
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
2 |
+
|
3 |
+
import { NotFoundComponent } from './not-found.component';
|
4 |
+
|
5 |
+
describe('NotFoundComponent', () => {
|
6 |
+
let component: NotFoundComponent;
|
7 |
+
let fixture: ComponentFixture<NotFoundComponent>;
|
8 |
+
|
9 |
+
beforeEach(async () => {
|
10 |
+
await TestBed.configureTestingModule({
|
11 |
+
imports: [NotFoundComponent]
|
12 |
+
})
|
13 |
+
.compileComponents();
|
14 |
+
|
15 |
+
fixture = TestBed.createComponent(NotFoundComponent);
|
16 |
+
component = fixture.componentInstance;
|
17 |
+
fixture.detectChanges();
|
18 |
+
});
|
19 |
+
|
20 |
+
it('should create', () => {
|
21 |
+
expect(component).toBeTruthy();
|
22 |
+
});
|
23 |
+
});
|
src/app/error_handling/not-found/not-found.component.ts
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Component } from '@angular/core';
|
2 |
+
import {RouterLink} from "@angular/router";
|
3 |
+
|
4 |
+
@Component({
|
5 |
+
selector: 'app-not-found',
|
6 |
+
standalone: true,
|
7 |
+
imports: [
|
8 |
+
RouterLink
|
9 |
+
],
|
10 |
+
templateUrl: './not-found.component.html',
|
11 |
+
styleUrl: './not-found.component.css'
|
12 |
+
})
|
13 |
+
export class NotFoundComponent {
|
14 |
+
|
15 |
+
}
|
src/app/state_management/models/leaderboard-entry.model.ts
CHANGED
@@ -6,4 +6,8 @@ export interface LeaderboardEntry {
|
|
6 |
precision: number;
|
7 |
recall: number;
|
8 |
f1_score: number;
|
|
|
|
|
|
|
|
|
9 |
}
|
|
|
6 |
precision: number;
|
7 |
recall: number;
|
8 |
f1_score: number;
|
9 |
+
predictions: string;
|
10 |
+
team: string;
|
11 |
+
is_public: boolean;
|
12 |
+
blob_url: string;
|
13 |
}
|
src/app/state_management/models/submission-entry.model.ts
CHANGED
@@ -9,4 +9,5 @@ export interface SubmissionEntry {
|
|
9 |
status: string;
|
10 |
time: string;
|
11 |
is_public: boolean;
|
|
|
12 |
}
|
|
|
9 |
status: string;
|
10 |
time: string;
|
11 |
is_public: boolean;
|
12 |
+
blob_url: string;
|
13 |
}
|
src/app/state_management/models/task.mode.ts
CHANGED
@@ -1,5 +1,3 @@
|
|
1 |
-
import {DatasetModel} from "./dataset.model";
|
2 |
-
|
3 |
export interface TaskModel {
|
4 |
name: string;
|
5 |
description: string;
|
|
|
|
|
|
|
1 |
export interface TaskModel {
|
2 |
name: string;
|
3 |
description: string;
|
src/app/state_management/services/app-state.service.ts
CHANGED
@@ -1,54 +1,128 @@
|
|
1 |
import { Injectable } from '@angular/core';
|
2 |
-
import {environment} from
|
3 |
-
import {BehaviorSubject,
|
4 |
-
import {HttpClient} from
|
5 |
-
import {StateModel} from
|
6 |
-
import {AuthenticationService} from
|
|
|
7 |
|
8 |
@Injectable({
|
9 |
-
providedIn: 'root'
|
10 |
})
|
11 |
export class AppStateService {
|
12 |
private readonly _apiUrl = environment.apiUrl;
|
13 |
|
14 |
-
private readonly _state = new BehaviorSubject<StateModel>(
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
}
|
23 |
-
);
|
24 |
|
25 |
public readonly state$ = this._state.asObservable();
|
26 |
|
27 |
-
constructor(public http: HttpClient,
|
28 |
-
|
29 |
-
|
30 |
-
this.
|
31 |
-
this.
|
32 |
-
this.
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
});
|
40 |
-
}
|
41 |
-
);
|
42 |
}
|
43 |
|
44 |
private _setState(state: StateModel): void {
|
45 |
this._state.next(state);
|
46 |
}
|
47 |
|
48 |
-
public getState()
|
49 |
return this._state.getValue();
|
50 |
}
|
51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
public submit(
|
53 |
modelName: string,
|
54 |
modelLink: string,
|
@@ -58,124 +132,71 @@ export class AppStateService {
|
|
58 |
dataset: string,
|
59 |
isPublic: boolean,
|
60 |
fileContent: string
|
61 |
-
)
|
62 |
-
return this.
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
public updateTasks() {
|
73 |
-
this.http.get(this._apiUrl + '/tasks').subscribe(
|
74 |
-
(data: any) => {
|
75 |
-
this._setState({
|
76 |
-
...this.getState(),
|
77 |
-
tasks: data
|
78 |
-
});
|
79 |
-
}
|
80 |
);
|
81 |
}
|
82 |
|
83 |
-
public
|
84 |
-
|
85 |
-
(data: any) => {
|
86 |
-
this._setState({
|
87 |
-
...this.getState(),
|
88 |
-
datasets: data
|
89 |
-
});
|
90 |
-
}
|
91 |
-
);
|
92 |
}
|
93 |
|
94 |
-
public
|
95 |
-
|
96 |
-
(data: any) => {
|
97 |
-
this._setState({
|
98 |
-
...this.getState(),
|
99 |
-
leaderboards: data
|
100 |
-
});
|
101 |
-
}
|
102 |
-
);
|
103 |
}
|
104 |
|
105 |
-
public
|
106 |
-
this.http.get(this._apiUrl + '/
|
107 |
-
(data: any) => {
|
108 |
-
this._setState({
|
109 |
-
...this.getState(),
|
110 |
-
submissions: data
|
111 |
-
});
|
112 |
-
}
|
113 |
-
);
|
114 |
}
|
115 |
|
116 |
-
public
|
117 |
-
this.
|
118 |
-
() => {
|
119 |
-
this.updateControlPanel();
|
120 |
-
}
|
121 |
-
);
|
122 |
}
|
123 |
|
124 |
-
public
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
this.authService.logout();
|
130 |
-
}
|
131 |
-
console.log(error);
|
132 |
-
return error;
|
133 |
-
}
|
134 |
-
))
|
135 |
-
.subscribe(
|
136 |
-
(data: any) => {
|
137 |
this._setState({
|
138 |
...this.getState(),
|
139 |
-
|
140 |
});
|
141 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
);
|
143 |
}
|
144 |
|
145 |
-
public
|
146 |
const headers = this.authService.getAuthHeaders();
|
147 |
-
this.
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
console.log(error);
|
153 |
-
return error;
|
154 |
-
}
|
155 |
-
))
|
156 |
-
.subscribe(
|
157 |
-
() => {
|
158 |
-
this.updateControlPanel();
|
159 |
-
}
|
160 |
-
);
|
161 |
}
|
162 |
|
163 |
-
public deleteSubmission(id: number){
|
164 |
const headers = this.authService.getAuthHeaders();
|
165 |
-
this.
|
166 |
-
.
|
167 |
-
|
168 |
-
|
169 |
-
this.authService.logout();
|
170 |
-
}
|
171 |
-
console.log(error);
|
172 |
-
return error;
|
173 |
-
}
|
174 |
-
))
|
175 |
-
.subscribe(
|
176 |
-
() => {
|
177 |
-
this.updateControlPanel();
|
178 |
-
}
|
179 |
);
|
180 |
}
|
181 |
}
|
|
|
1 |
import { Injectable } from '@angular/core';
|
2 |
+
import { environment } from '../../../environments/environment';
|
3 |
+
import {BehaviorSubject, Observable, OperatorFunction, shareReplay, throwError, timer} from 'rxjs';
|
4 |
+
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
5 |
+
import { StateModel } from '../models/state.model';
|
6 |
+
import { AuthenticationService } from './authentication.service';
|
7 |
+
import { catchError, retry } from 'rxjs/operators';
|
8 |
|
9 |
@Injectable({
|
10 |
+
providedIn: 'root',
|
11 |
})
|
12 |
export class AppStateService {
|
13 |
private readonly _apiUrl = environment.apiUrl;
|
14 |
|
15 |
+
private readonly _state = new BehaviorSubject<StateModel>({
|
16 |
+
leaderboards: [],
|
17 |
+
submissions: [],
|
18 |
+
datasets: [],
|
19 |
+
tasks: [],
|
20 |
+
controlPanelSubmissions: [],
|
21 |
+
adminSessionStatus: '',
|
22 |
+
});
|
|
|
|
|
23 |
|
24 |
public readonly state$ = this._state.asObservable();
|
25 |
|
26 |
+
constructor(public http: HttpClient, public authService: AuthenticationService) {
|
27 |
+
console.log(this._apiUrl);
|
28 |
+
this.refreshTasks();
|
29 |
+
this.refreshDatasets();
|
30 |
+
this.refreshLeaderboards();
|
31 |
+
this.refreshSubmissions();
|
32 |
+
authService.$authStatus.subscribe((status: string) => {
|
33 |
+
this._setState({
|
34 |
+
...this.getState(),
|
35 |
+
adminSessionStatus: status,
|
36 |
+
});
|
37 |
+
});
|
|
|
|
|
|
|
38 |
}
|
39 |
|
40 |
private _setState(state: StateModel): void {
|
41 |
this._state.next(state);
|
42 |
}
|
43 |
|
44 |
+
public getState(): StateModel {
|
45 |
return this._state.getValue();
|
46 |
}
|
47 |
|
48 |
+
private makeRequest<T>(request: Observable<T>, stateKey?: keyof StateModel, callback?: (data: any) => void) {
|
49 |
+
let obs = request.pipe(
|
50 |
+
this.retryStrategy(),
|
51 |
+
shareReplay(1),
|
52 |
+
catchError(this.handleRequestError.bind(this))
|
53 |
+
);
|
54 |
+
obs.subscribe(
|
55 |
+
(data) => {
|
56 |
+
if (callback) {
|
57 |
+
callback(data);
|
58 |
+
} else if (stateKey) {
|
59 |
+
this._setState({
|
60 |
+
...this.getState(),
|
61 |
+
[stateKey]: data
|
62 |
+
});
|
63 |
+
}
|
64 |
+
}
|
65 |
+
);
|
66 |
+
return obs;
|
67 |
+
}
|
68 |
+
|
69 |
+
private handleRequestError(error: any): Observable<never> {
|
70 |
+
let errorMessage = 'An unknown error occurred!';
|
71 |
+
|
72 |
+
if (error.error instanceof ErrorEvent) {
|
73 |
+
errorMessage = `Client Error: ${error.error.message}`;
|
74 |
+
} else {
|
75 |
+
switch (error.status) {
|
76 |
+
case 400:
|
77 |
+
errorMessage = `Bad Request: ${error.message}`;
|
78 |
+
break;
|
79 |
+
case 401:
|
80 |
+
errorMessage = 'Unauthorized: Please log in again.';
|
81 |
+
this.authService.logout();
|
82 |
+
break;
|
83 |
+
case 404:
|
84 |
+
errorMessage = `Not Found: The requested resource was not found.`;
|
85 |
+
break;
|
86 |
+
case 500:
|
87 |
+
errorMessage = `Internal Server Error: Please try again later.`;
|
88 |
+
break;
|
89 |
+
case 503:
|
90 |
+
errorMessage = `Service Unavailable: Please try again later.`;
|
91 |
+
break;
|
92 |
+
default:
|
93 |
+
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
|
94 |
+
break;
|
95 |
+
}
|
96 |
+
}
|
97 |
+
// Rethrow the error so it can be handled globally
|
98 |
+
return throwError(() => new Error(errorMessage));
|
99 |
+
}
|
100 |
+
|
101 |
+
// Improved retry strategy with exponential backoff and jitter
|
102 |
+
private retryStrategy<T>() {
|
103 |
+
return <OperatorFunction<T, T>>((source) =>
|
104 |
+
source.pipe(
|
105 |
+
retry({
|
106 |
+
count: 3, // Maximum of 3 retry attempts
|
107 |
+
delay: (error, retryCount) => {
|
108 |
+
if (![500, 503].includes(error.status)) {
|
109 |
+
// Do not retry for errors other than 500 and 503
|
110 |
+
return throwError(() => error);
|
111 |
+
}
|
112 |
+
// Exponential backoff with jitter
|
113 |
+
const jitter = Math.random() * 500; // Jitter value between 0-500ms
|
114 |
+
const backoffDelay = Math.pow(2, retryCount) * 1000 + jitter; // Exponential backoff
|
115 |
+
console.log(`Retrying request after ${backoffDelay}ms (attempt #${retryCount})`);
|
116 |
+
return timer(backoffDelay);
|
117 |
+
}
|
118 |
+
})
|
119 |
+
)
|
120 |
+
);
|
121 |
+
}
|
122 |
+
|
123 |
+
// ========================
|
124 |
+
// Public API
|
125 |
+
// ========================
|
126 |
public submit(
|
127 |
modelName: string,
|
128 |
modelLink: string,
|
|
|
132 |
dataset: string,
|
133 |
isPublic: boolean,
|
134 |
fileContent: string
|
135 |
+
) {
|
136 |
+
return this.makeRequest(
|
137 |
+
this.http.post<Object>(`${this._apiUrl}/submission/${task}/${dataset}`, {
|
138 |
+
modelName,
|
139 |
+
modelLink,
|
140 |
+
teamName,
|
141 |
+
contactEmail,
|
142 |
+
isPublic,
|
143 |
+
fileContent,
|
144 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
);
|
146 |
}
|
147 |
|
148 |
+
public refreshTasks() {
|
149 |
+
return this.makeRequest(this.http.get(this._apiUrl + '/tasks'), 'tasks');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
}
|
151 |
|
152 |
+
public refreshDatasets() {
|
153 |
+
return this.makeRequest(this.http.get(this._apiUrl + '/datasets'), 'datasets');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
}
|
155 |
|
156 |
+
public refreshLeaderboards() {
|
157 |
+
return this.makeRequest(this.http.get(this._apiUrl + '/leaderboards'), 'leaderboards');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
}
|
159 |
|
160 |
+
public refreshSubmissions() {
|
161 |
+
return this.makeRequest(this.http.get(this._apiUrl + '/submissions'), 'submissions');
|
|
|
|
|
|
|
|
|
162 |
}
|
163 |
|
164 |
+
public authenticate(password: string) {
|
165 |
+
let obs = this.authService.login(password);
|
166 |
+
obs.subscribe(
|
167 |
+
next => {
|
168 |
+
console.log('Login successful');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
this._setState({
|
170 |
...this.getState(),
|
171 |
+
adminSessionStatus: 'authenticated',
|
172 |
});
|
173 |
+
});
|
174 |
+
return obs;
|
175 |
+
}
|
176 |
+
|
177 |
+
public refreshControlPanel() {
|
178 |
+
const headers = this.authService.getAuthHeaders();
|
179 |
+
return this.makeRequest(
|
180 |
+
this.http.get(`${this._apiUrl}/control-panel-submissions`, { headers }),
|
181 |
+
'controlPanelSubmissions'
|
182 |
);
|
183 |
}
|
184 |
|
185 |
+
public updateSubmission(entry: any) {
|
186 |
const headers = this.authService.getAuthHeaders();
|
187 |
+
return this.makeRequest(
|
188 |
+
this.http.put(`${this._apiUrl}/submission/${entry.id}`, entry, { headers }),
|
189 |
+
undefined,
|
190 |
+
() => this.refreshControlPanel() // Refresh control panel after update
|
191 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
}
|
193 |
|
194 |
+
public deleteSubmission(id: number) {
|
195 |
const headers = this.authService.getAuthHeaders();
|
196 |
+
return this.makeRequest(
|
197 |
+
this.http.delete(`${this._apiUrl}/submission/${id}`, { headers }),
|
198 |
+
undefined,
|
199 |
+
() => this.refreshControlPanel() // Refresh control panel after deletion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
);
|
201 |
}
|
202 |
}
|
src/app/state_management/services/authentication.service.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import {Injectable} from '@angular/core';
|
2 |
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
3 |
-
import {BehaviorSubject, catchError, Observable, tap, throwError} from "rxjs";
|
4 |
import {environment} from "../../../environments/environment";
|
5 |
|
6 |
@Injectable({
|
@@ -41,21 +41,18 @@ export class AuthenticationService {
|
|
41 |
const payload = new URLSearchParams();
|
42 |
payload.set('username', 'admin'); // Username is not used in this case
|
43 |
payload.set('password', password);
|
44 |
-
|
|
|
|
|
45 |
// Send POST request to the backend
|
46 |
return this.http.post<{ access_token: string; token_type: string }>
|
47 |
-
(
|
48 |
-
this.apiUrl + '/token', payload.toString(),
|
49 |
-
{
|
50 |
-
headers: {
|
51 |
-
'Content-Type': 'application/x-www-form-urlencoded', // Set content type for form data
|
52 |
-
},
|
53 |
-
}).pipe(
|
54 |
tap(response => {
|
55 |
this.token = response.access_token;
|
56 |
localStorage.setItem('auth_token', this.token);
|
57 |
this.$authStatus.next('authenticated');
|
58 |
}),
|
|
|
59 |
catchError(error => {
|
60 |
if (error.status === 401) {
|
61 |
this.$authStatus.next('wrong password');
|
@@ -68,7 +65,6 @@ export class AuthenticationService {
|
|
68 |
);
|
69 |
}
|
70 |
|
71 |
-
|
72 |
public logout(): void {
|
73 |
this.token = null;
|
74 |
localStorage.removeItem('auth_token');
|
|
|
1 |
import {Injectable} from '@angular/core';
|
2 |
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
3 |
+
import {BehaviorSubject, catchError, Observable, shareReplay, tap, throwError} from "rxjs";
|
4 |
import {environment} from "../../../environments/environment";
|
5 |
|
6 |
@Injectable({
|
|
|
41 |
const payload = new URLSearchParams();
|
42 |
payload.set('username', 'admin'); // Username is not used in this case
|
43 |
payload.set('password', password);
|
44 |
+
let headers = new HttpHeaders({
|
45 |
+
'Content-Type': 'application/x-www-form-urlencoded',
|
46 |
+
});
|
47 |
// Send POST request to the backend
|
48 |
return this.http.post<{ access_token: string; token_type: string }>
|
49 |
+
(this.apiUrl + '/token', payload.toString(), { headers }).pipe(
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
tap(response => {
|
51 |
this.token = response.access_token;
|
52 |
localStorage.setItem('auth_token', this.token);
|
53 |
this.$authStatus.next('authenticated');
|
54 |
}),
|
55 |
+
shareReplay(1),
|
56 |
catchError(error => {
|
57 |
if (error.status === 401) {
|
58 |
this.$authStatus.next('wrong password');
|
|
|
65 |
);
|
66 |
}
|
67 |
|
|
|
68 |
public logout(): void {
|
69 |
this.token = null;
|
70 |
localStorage.removeItem('auth_token');
|