Ahmad Shallouf commited on
Commit
27e40c0
1 Parent(s): 024b436

added new modifications

Browse files
Files changed (31) hide show
  1. .DS_Store +0 -0
  2. src/.DS_Store +0 -0
  3. src/app/.DS_Store +0 -0
  4. src/app/app.config.ts +4 -2
  5. src/app/app.routes.ts +5 -1
  6. src/app/components/.DS_Store +0 -0
  7. src/app/components/body/.DS_Store +0 -0
  8. src/app/components/body/body.component.html +1 -1
  9. src/app/components/body/control-panel/admin-login/admin-login.component.ts +12 -3
  10. src/app/components/body/control-panel/control-panel.component.html +0 -2
  11. src/app/components/body/control-panel/control-panel.component.ts +2 -2
  12. src/app/components/body/leaderboards/leaderboard/leaderboard.component.html +25 -43
  13. src/app/components/body/leaderboards/leaderboard/leaderboard.component.ts +20 -9
  14. src/app/components/body/submissions/submissions.component.html +26 -45
  15. src/app/components/body/submissions/submissions.component.ts +19 -5
  16. src/app/components/body/submitting/submitting-guide/submitting-guide.component.css +0 -0
  17. src/app/components/body/submitting/submitting-guide/submitting-guide.component.html +19 -0
  18. src/app/components/body/submitting/submitting-guide/submitting-guide.component.spec.ts +23 -0
  19. src/app/components/body/submitting/submitting-guide/submitting-guide.component.ts +12 -0
  20. src/app/components/body/submitting/submitting.component.html +4 -1
  21. src/app/components/body/submitting/submitting.component.ts +29 -18
  22. src/app/error_handling/global-error-handler.service.ts +81 -0
  23. src/app/error_handling/not-found/not-found.component.css +25 -0
  24. src/app/error_handling/not-found/not-found.component.html +5 -0
  25. src/app/error_handling/not-found/not-found.component.spec.ts +23 -0
  26. src/app/error_handling/not-found/not-found.component.ts +15 -0
  27. src/app/state_management/models/leaderboard-entry.model.ts +4 -0
  28. src/app/state_management/models/submission-entry.model.ts +1 -0
  29. src/app/state_management/models/task.mode.ts +0 -2
  30. src/app/state_management/services/app-state.service.ts +150 -129
  31. 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 { ApplicationConfig } 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
 
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: ControlPanelComponent},
 
 
 
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.updateControlPanel();
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.forceUpdateSubmission(entry);
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]="getTextDownloadURL(entry.predictions)" target="_blank" download="{{entry.model + '-predictions.txt'}}">Download</a>
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
- <ng-container matColumnDef="f1">
69
- <mat-header-cell
70
- *matHeaderCellDef
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
- 'f1'
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.stateService.updateLeaderboards();
76
  }
77
 
78
  getTextDownloadURL(prediction: string) {
@@ -80,6 +91,6 @@ export class LeaderboardComponent implements OnInit {
80
  }
81
 
82
  refresh() {
83
- this.stateService.updateLeaderboards();
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]="getTextDownloadURL(entry.predictions)" target="_blank" download="{{entry.model + '-predictions.txt'}}">Download</a>
 
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.updateSubmissions();
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: (data) => {
90
- this.message = 'Submission successful';
 
91
  },
92
- error: (err) => {
93
- // show error message that includes the error code
94
- this.message = 'Submission failed: ' + err.message;
 
 
 
 
 
 
 
 
 
95
  }
96
  }
97
  );
98
  }
99
 
100
- onFileSelected(event : any) {
101
  const file: File = event.target.files[0];
102
  if (file) {
103
  this.chosenFileName = file.name;
104
- // read file as text
105
- const reader = new FileReader();
106
- reader.onload = (e: any) => {
107
- this.fileContent = e.target.result;
108
- };
109
- reader.readAsText(file);
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 "../../../environments/environment";
3
- import {BehaviorSubject, catchError, Observable} from "rxjs";
4
- import {HttpClient} from "@angular/common/http";
5
- import {StateModel} from "../models/state.model";
6
- import {AuthenticationService} from "./authentication.service";
 
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
- leaderboards: [],
17
- submissions: [],
18
- datasets: [],
19
- tasks: [],
20
- controlPanelSubmissions: [],
21
- adminSessionStatus: ''
22
- }
23
- );
24
 
25
  public readonly state$ = this._state.asObservable();
26
 
27
- constructor(public http: HttpClient,
28
- public authService: AuthenticationService) {
29
- console.log(this._apiUrl)
30
- this.updateTasks();
31
- this.updateDatasets();
32
- this.updateLeaderboards();
33
- this.updateSubmissions();
34
- authService.$authStatus.subscribe(
35
- (status: string) => {
36
- this._setState({
37
- ...this.getState(),
38
- adminSessionStatus: status
39
- });
40
- }
41
- );
42
  }
43
 
44
  private _setState(state: StateModel): void {
45
  this._state.next(state);
46
  }
47
 
48
- public getState() : StateModel {
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
- ) : Observable<Object> {
62
- return this.http.post(this._apiUrl + '/submission/' + task + '/' + dataset, {
63
- modelName: modelName,
64
- modelLink: modelLink,
65
- teamName: teamName,
66
- contactEmail: contactEmail,
67
- isPublic: isPublic,
68
- fileContent: fileContent
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 updateDatasets() {
84
- this.http.get(this._apiUrl + '/datasets').subscribe(
85
- (data: any) => {
86
- this._setState({
87
- ...this.getState(),
88
- datasets: data
89
- });
90
- }
91
- );
92
  }
93
 
94
- public updateLeaderboards() {
95
- this.http.get(this._apiUrl + '/leaderboards').subscribe(
96
- (data: any) => {
97
- this._setState({
98
- ...this.getState(),
99
- leaderboards: data
100
- });
101
- }
102
- );
103
  }
104
 
105
- public updateSubmissions() {
106
- this.http.get(this._apiUrl + '/submissions').subscribe(
107
- (data: any) => {
108
- this._setState({
109
- ...this.getState(),
110
- submissions: data
111
- });
112
- }
113
- );
114
  }
115
 
116
- public authenticate(password : string) {
117
- this.authService.login(password).subscribe(
118
- () => {
119
- this.updateControlPanel();
120
- }
121
- );
122
  }
123
 
124
- public updateControlPanel() {
125
- const headers = this.authService.getAuthHeaders();
126
- this.http.get(`${this._apiUrl}/controlPanelSubmissions`, { headers }).pipe(
127
- catchError(error => {
128
- if (error.status === 401) {
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
- controlPanelSubmissions: data
140
  });
141
- }
 
 
 
 
 
 
 
 
142
  );
143
  }
144
 
145
- public forceUpdateSubmission(entry: any){
146
  const headers = this.authService.getAuthHeaders();
147
- this.http.put(`${this._apiUrl}/controlPanelSubmission/${entry.id}`, entry, { headers }).pipe(
148
- catchError(error => {
149
- if (error.status === 401) {
150
- this.authService.logout();
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.http.delete(`${this._apiUrl}/controlPanelSubmission/` + id, { headers })
166
- .pipe(
167
- catchError(error => {
168
- if (error.status === 401) {
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');