diff --git a/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.html b/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.html index d64c46416..544b038e9 100644 --- a/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.html +++ b/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.html @@ -1,35 +1,119 @@ -
-
- -
-
- - No Results.. -
-
- - - - - - - - - - - - - - -
-
Rank
-
-
Participant Team
-
{{key}}
-
-
Last Submission at - -
-
{{initial_ranking[key.submission__participant_team__team_name]}}
{{key.submission__participant_team__team_name}}
{{score | number : '1.2-2'}}
{{ key.submission__submitted_at_formatted }}
+
+
+
+
+
+ Description +
+
+
+
+
+
+ Page is Outdated, Click to update  + +
+
+
+ Please select from following phases! +
+
+
+
+ + +
+
+
+ +
+
+
+
+
No phase selected.
+
+ + B - Baseline submission + +
+ + + + + + + + + + + + + + + + + + +
+ + Rank + + + + + + + + Participant Team + + + + + + + + {{key}} + + + + + + + + Last submission at + + + + + +
{{initial_ranking[key.submission__participant_team__team_name]}}{{key.submission__participant_team__team_name}} + + + B + + + {{score | number : '1.2-2'}}
{{ key.submission__submitted_at_formatted }}
+
+

No results to show!

+
+
+
+
+
diff --git a/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.scss b/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.scss index 4408d6f66..8266a9c83 100644 --- a/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.scss +++ b/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.scss @@ -71,3 +71,14 @@ } } } + +.ev-card-body { + .row-lr-margin { + margin-bottom: 20px; + } + + .mat-standard-chip { + padding: 3px 5px; + font-size: 16px; + } +} diff --git a/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts b/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts index 7c2fa135d..f2ab47d67 100644 --- a/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts +++ b/src/app/components/challenge/challengeleaderboard/challengeleaderboard.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, QueryList, ViewChildren, AfterViewInit } from '@angular/core'; +import { Component, OnInit, QueryList, ViewChildren, AfterViewInit, Self } from '@angular/core'; import { AuthService } from '../../../services/auth.service'; import { ApiService } from '../../../services/api.service'; import { GlobalService } from '../../../services/global.service'; @@ -58,11 +58,26 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { */ filteredPhaseSplits = []; + /** + * Phase selection type (radio button or select box) + */ + phaseSelectionType = 'selectBox'; + + /** + * Select box list type + */ + phaseSelectionListType = 'phaseSplit'; + /** * Leaderboard entries list */ leaderboard = []; + /** + * Show leaderboard updates + */ + showLeaderboardUpdate = false; + /** * Currently selected phase split's id */ @@ -98,6 +113,20 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { */ entryHighlighted: any = null; + /** + * An interval for fetching the leaderboard data in every 5 seconds + */ + pollingInterval: any; + + /** + * Challenge phase visibility + */ + challengePhaseVisibility = { + owner_and_host: 1, + host: 2, + public: 3, + }; + /** * Constructor. * @param route ActivatedRoute Injection. @@ -146,21 +175,12 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { */ filterPhases() { if (this.phases.length > 0 && this.phaseSplits.length > 0) { - const TEMPSPLITS = []; - for (let i = 0; i < this.phases.length; i++) { - if (this.phases[i]['leaderboard_public']) { - const TEMP = this.phases[i]; - TEMP['phase_split'] = null; - for (let j = 0; j < this.phaseSplits.length; j++) { - if (this.phaseSplits[j]['challenge_phase'] === TEMP['id'] && this.phaseSplits[j]['visibility'] === 3) { - const TEMP_COPY = Object.assign({}, TEMP); - TEMP_COPY['phase_split'] = this.phaseSplits[j]; - TEMPSPLITS.push(TEMP_COPY); - } - } + for (let i = 0; i < this.phaseSplits.length; i++) { + if (this.phaseSplits[i].visibility !== this.challengePhaseVisibility.public) { + this.phaseSplits[i].showPrivate = true; } } - this.filteredPhaseSplits = TEMPSPLITS; + this.filteredPhaseSplits = this.phaseSplits; setTimeout(() => { this.checkUrlParams(); }, 100); @@ -172,14 +192,9 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { */ checkUrlParams() { this.route.params.subscribe(params => { - console.log(params); if (params['split']) { this.selectedPhaseSplitId = params['split']; this.selectPhaseSplitId(this.selectedPhaseSplitId, this); - } else { - if (this.filteredPhaseSplits.length > 0) { - this.router.navigate([this.filteredPhaseSplits[0]['phase_split']['id']], {relativeTo: this.route}); - } } }); } @@ -192,12 +207,12 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { selectPhaseSplitId(id, self) { let i = 0; for (i = 0; i < self.filteredPhaseSplits.length; i++) { - if (parseInt(id, 10) === self.filteredPhaseSplits[i]['phase_split']['id']) { + if (parseInt(id, 10) === self.filteredPhaseSplits[i]['id']) { self.selectedPhaseSplit = self.filteredPhaseSplits[i]; const checkViewInit = () => { if (self.viewInit) { self.components.map((item) => { - item.selectPhase(self.selectedPhaseSplit); + item.selectPhaseSplit(self.selectedPhaseSplit, 'selectBox', 'phaseSplit'); }); } else { setTimeout(() => { @@ -221,18 +236,18 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { const SELF = this; return (phaseSplit) => { if (SELF.router.url.endsWith('leaderboard')) { - SELF.router.navigate(['../' + phaseSplit['phase_split']['id']], {relativeTo: this.route}); - } else if (SELF.router.url.indexOf(phaseSplit['phase_split']['id']) < 0 && SELF.router.url.split('/').length === 5) { - SELF.router.navigate(['../' + phaseSplit['phase_split']['id']], {relativeTo: this.route}); - } else if (SELF.router.url.indexOf(phaseSplit['phase_split']['id']) < 0 && SELF.router.url.split('/').length === 6) { - SELF.router.navigate(['../../' + phaseSplit['phase_split']['id']], {relativeTo: this.route}); - } else { - SELF.selectedPhaseSplit = phaseSplit; - if (SELF.selectedPhaseSplit['phase_split']) { - SELF.fetchLeaderboard(SELF.selectedPhaseSplit['phase_split']['id']); - } + SELF.router.navigate([phaseSplit['id']], {relativeTo: this.route}); + } else if (SELF.router.url.split('/').length === 5) { + SELF.router.navigate(['../' + phaseSplit['id']], {relativeTo: this.route}); + } else if (SELF.router.url.split('/').length === 6) { + SELF.router.navigate(['../../' + phaseSplit['id']], {relativeTo: this.route}); + } + SELF.selectedPhaseSplit = phaseSplit; + if (SELF.selectedPhaseSplit) { + SELF.fetchLeaderboard(SELF.selectedPhaseSplit['id']); } }; + } /** @@ -251,7 +266,6 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { } self.leaderboard = leaderboard.slice(); self.sortLeaderboard(); - self.route.params.subscribe(params => { if (params['entry']) { self.entryHighlighted = params['entry']; @@ -261,14 +275,6 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { item['is_highlighted'] = true; } }); - } else { - self.challengeService.currentParticipantTeams.subscribe((teams) => { - teams.map((item) => { - if (self.challenge && item['challenge'] && item['challenge']['id'] === self.challenge['id']) { - self.router.navigate([item['participant_team']['team_name']], {relativeTo: this.route}); - } - }); - }); } }); } @@ -310,23 +316,99 @@ export class ChallengeleaderboardComponent implements OnInit, AfterViewInit { return 0; } + /** + * Sort the rank and participant team leaderboard column + * @param sortColumn sort column ('rank' or 'string') + */ + sortNonMetricsColumn (sortColumn) { + const SELF = this; + if (SELF.sortColumn === sortColumn) { + SELF.reverseSort = !SELF.reverseSort; + } else { + SELF.reverseSort = false; + } + SELF.sortColumn = sortColumn; + SELF.sortLeaderboard(); + } + + /** + * To sort by the metrics column + * @param index Schema labels index + */ + sortMetricsColumn (index) { + const SELF = this; + if (SELF.sortColumn === 'number' && SELF.columnIndexSort === index) { + SELF.reverseSort = !SELF.reverseSort; + } else { + SELF.reverseSort = false; + } + SELF.sortColumn = 'number'; + SELF.columnIndexSort = index; + SELF.sortLeaderboard(); + } + /** * Fetch leaderboard for a phase split - * @param phaseSplitId id of the phase split + * @param phaseSplitId id of the phase split */ fetchLeaderboard(phaseSplitId) { const API_PATH = this.endpointsService.challengeLeaderboardURL(phaseSplitId); const SELF = this; + clearInterval(SELF.pollingInterval); + SELF.leaderboard = []; + SELF.showLeaderboardUpdate = false; this.apiService.getUrl(API_PATH).subscribe( data => { SELF.updateLeaderboardResults(data['results'], SELF); + SELF.startLeaderboard(phaseSplitId); }, err => { SELF.globalService.handleApiError(err); }, - () => { - console.log('Fetched leaderboard for split:', phaseSplitId); - } + () => {} + ); + } + + /** + * Call leaderboard API in the interval of 5 seconds + * @param phaseSplitId id of the phase split + */ + startLeaderboard(phaseSplitId) { + const API_PATH = this.endpointsService.challengeLeaderboardURL(phaseSplitId); + const SELF = this; + clearInterval(SELF.pollingInterval); + SELF.pollingInterval = setInterval(function() { + SELF.apiService.getUrl(API_PATH, true, false).subscribe( + data => { + if (SELF.leaderboard.length !== data['results'].length) { + SELF.showLeaderboardUpdate = true; + } + }, + err => { + SELF.globalService.handleApiError(err); + }, + () => {} + ); + }, 5000); + } + + /** + * Refresh leaderboard if there is any update in data + */ + refreshLeaderboard() { + const API_PATH = this.endpointsService.challengeLeaderboardURL(this.selectedPhaseSplit['id']); + const SELF = this; + SELF.leaderboard = []; + SELF.showLeaderboardUpdate = false; + SELF.apiService.getUrl(API_PATH).subscribe( + data => { + SELF.updateLeaderboardResults(data['results'], SELF); + SELF.startLeaderboard(SELF.selectedPhaseSplit['id']); + }, + err => { + SELF.globalService.handleApiError(err); + }, + () => {} ); } } diff --git a/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html b/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html index f7f47cec4..2cd883bda 100644 --- a/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html +++ b/src/app/components/challenge/challengesubmissions/challengesubmissions.component.html @@ -15,6 +15,7 @@
diff --git a/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts b/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts index c4066bb63..c29e14a8e 100644 --- a/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts +++ b/src/app/components/challenge/challengesubmissions/challengesubmissions.component.ts @@ -115,6 +115,11 @@ export class ChallengesubmissionsComponent implements OnInit, AfterViewInit { */ phaseSelectionType = 'selectBox'; + /** + * Select box list type + */ + phaseSelectionListType = 'phase'; + /** * @param showPagination Is pagination * @param paginationMessage Pagination message diff --git a/src/app/components/challenge/challengesubmit/challengesubmit.component.html b/src/app/components/challenge/challengesubmit/challengesubmit.component.html index b8faf76a8..d73f18ac2 100644 --- a/src/app/components/challenge/challengesubmit/challengesubmit.component.html +++ b/src/app/components/challenge/challengesubmit/challengesubmit.component.html @@ -58,249 +58,250 @@ - -
  • For more commands, please refer to - evalai-cli - documentation. -
  • - -
    -
    + +
  • For more commands, please refer to + evalai-cli + documentation. +
  • +
    + + -
    - - Submission limits -

    - - Team name: - {{phaseRemainingSubmissions.participant_team}} - - - - - - - - - - - - - - - - - - -
    Phase detailsRemaining submissionsSubmission command
    {{key.name}} -
    - Start date: {{key.start_date | date:'medium'}} -
    - End date: {{key.end_date | date:'medium'}} -
    - - Today: - - {{key.limits.remaining_submissions_today_count}}
    - - This month: - - {{key.limits.remaining_submissions_this_month_count}}
    - - Total: - - {{key.limits.remaining_submissions_count}} -
    - {{key.limits.message}}
    - Next submission will be available in
    -
    - {{phaseRemainingSubmissionsCountdown[key.id].days}} - Days, {{phaseRemainingSubmissionsCountdown[key.id].hours}} - hours : {{phaseRemainingSubmissionsCountdown[key.id].minutes}} - minutes : - {{phaseRemainingSubmissionsCountdown[key.id].seconds}} seconds -
    -
    - {{key.limits.message}}
    - - {{key.limits.maxExceededMessage}} - -
    - $ evalai push <image>:<tag> --phase {{key.slug}} - -
    -
    - -
    -
    - - Please fill the following fields to submit +
    + + Submission limits +

    + + Team name: + {{phaseRemainingSubmissions.participant_team}} + + + + + + + + + + + + + + + + + + +
    Phase detailsRemaining submissionsSubmission command
    {{key.name}} +
    + Start date: {{key.start_date | date:'medium'}} +
    + End date: {{key.end_date | date:'medium'}} +
    + + Today: - -
    -
    -
    -
      - Select phase: - - -
      -
      -
      - -
      -
      - -
      -
      -
      - -
      -
      -
      - -
      -
      -
      -
      -
      - -
      -
      - -
      -
      + {{key.limits.remaining_submissions_today_count}}
      + + This month: + + {{key.limits.remaining_submissions_this_month_count}}
      + + Total: + + {{key.limits.remaining_submissions_count}} +
    + {{key.limits.message}}
    + Next submission will be available in
    +
    + {{phaseRemainingSubmissionsCountdown[key.id].days}} + Days, {{phaseRemainingSubmissionsCountdown[key.id].hours}} + hours : {{phaseRemainingSubmissionsCountdown[key.id].minutes}} + minutes : + {{phaseRemainingSubmissionsCountdown[key.id].seconds}} seconds +
    +
    + {{key.limits.message}}
    + + {{key.limits.maxExceededMessage}} + +
    + $ evalai push <image>:<tag> --phase {{key.slug}} + +
    +
    -
    -
    - -
    -
    +
    +
    + + Please fill the following fields to submit + +
    +
    +
    +
    +
      + Select phase: + -
      - {{submissionError}}

      -
      +
      +
      +
      + +
      +
      - +
      +
      +
      +
      -
    +
    -
    -
    -
    -
    - - - {{ selectedPhaseSubmissions.maxExceededMessage }} - - +
    +
    +
    -
    -
    -
    -
    - - - Today's remaining Submissions: - - - - - {{selectedPhaseSubmissions.remainingSubmissions.remaining_submissions_today_count}} - - -
    -
    - - - Monthly remaining submissions: - - - - - {{selectedPhaseSubmissions.remainingSubmissions.remaining_submissions_this_month_count}} - - -
    -
    - - - Total remaining submissions: - - - - - {{selectedPhaseSubmissions.remainingSubmissions.remaining_submissions_count}} - - +
    +
    +
    -
    -
    -
    -
    - - Message: - {{selectedPhaseSubmissions.clockMessage.message}} -
    -
    - Next set of submissions will be available after: +
    +
    +
    -
    - {{days}} Days, {{hours}} - hours : {{minutes}} minutes : {{seconds}} seconds +
    + +
    + {{submissionError}}

    +
    + +
    + +
    +
    + +
    +
    +
    + + + {{ selectedPhaseSubmissions.maxExceededMessage }} + + +
    +
    +
    + +
    +
    +
    + + + Today's remaining Submissions: + + + + + {{selectedPhaseSubmissions.remainingSubmissions.remaining_submissions_today_count}} + + +
    +
    + + + Monthly remaining submissions: + + + + + {{selectedPhaseSubmissions.remainingSubmissions.remaining_submissions_this_month_count}} + + +
    +
    + + + Total remaining submissions: + + + + + {{selectedPhaseSubmissions.remainingSubmissions.remaining_submissions_count}} + +
    + +
    +
    +
    + + Message: + {{selectedPhaseSubmissions.clockMessage.message}} +
    +
    + Next set of submissions will be available after: +
    +
    + {{days}} Days, {{hours}} + hours : {{minutes}} minutes : {{seconds}} seconds +
    +
    +
    +
    +
    +
    diff --git a/src/app/components/challenge/challengesubmit/challengesubmit.component.ts b/src/app/components/challenge/challengesubmit/challengesubmit.component.ts index 7069a38d4..18fc89ad6 100644 --- a/src/app/components/challenge/challengesubmit/challengesubmit.component.ts +++ b/src/app/components/challenge/challengesubmit/challengesubmit.component.ts @@ -101,6 +101,11 @@ export class ChallengesubmitComponent implements OnInit { */ phaseSelectionType = 'radioButton'; + /** + * Select box list type + */ + phaseSelectionListType = 'phase'; + /** * Api call inside the modal to edit the submission guidelines */ diff --git a/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.html b/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.html index 1d35ae231..976594003 100644 --- a/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.html +++ b/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.html @@ -11,6 +11,7 @@
    diff --git a/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts b/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts index c47db40a7..3811b35df 100644 --- a/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts +++ b/src/app/components/challenge/challengeviewallsubmissions/challengeviewallsubmissions.component.ts @@ -105,6 +105,11 @@ export class ChallengeviewallsubmissionsComponent implements OnInit, AfterViewIn */ phaseSelectionType = 'selectBox'; + /** + * Select box list type + */ + phaseSelectionListType = 'phase'; + /** * Filter query as participant team name */ diff --git a/src/app/components/utility/selectphase/selectphase.component.html b/src/app/components/utility/selectphase/selectphase.component.html index 9cd334c40..3308e691e 100644 --- a/src/app/components/utility/selectphase/selectphase.component.html +++ b/src/app/components/utility/selectphase/selectphase.component.html @@ -32,7 +32,7 @@ - + Phase @@ -55,3 +55,33 @@ + + + + Challenge Phase + + + + Phase:{{phaseName}}, + Split: {{splitName}}   + + + + Private + + + + + + Phase:{{key.challenge_phase_name}}, + Split: {{key.dataset_split_name}}   + + + + Private + + + + + + diff --git a/src/app/components/utility/selectphase/selectphase.component.ts b/src/app/components/utility/selectphase/selectphase.component.ts index 8950bba62..5d05727ac 100644 --- a/src/app/components/utility/selectphase/selectphase.component.ts +++ b/src/app/components/utility/selectphase/selectphase.component.ts @@ -22,6 +22,21 @@ export class SelectphaseComponent implements OnInit, OnChanges { */ @Input() phaseSelected: any; + /** + * Selected phase split callback + */ + @Input() phaseSplitSelected: any; + + /** + * Phase selection type (radio button or select box) + */ + @Input() phaseSelectionType: string; + + /** + * Phase selection list type (phase or phase split) + */ + @Input () phaseSelectionListType: string; + /** * Selected phase name */ @@ -33,9 +48,19 @@ export class SelectphaseComponent implements OnInit, OnChanges { phaseVisibility = false; /** - * Phase selection type (radio button or select box) + * Currently selected phase */ - @Input() phaseSelectionType: string; + selectedPhase: any = null; + + /** + * Selected split name + */ + splitName = ''; + + /** + * Selected phase split + */ + selectedPhaseSplit = ''; /** * Challenge object @@ -76,6 +101,22 @@ export class SelectphaseComponent implements OnInit, OnChanges { this.phaseSelected(phase); } + /** + * Select a particular phase split. + * @param phaseSplit phase split to be selected. + * @param phaseSelectionType phase selection type (radio button or select box). + * @param phaseSelectionListType phase selection list type (phase or phase split) + */ + selectPhaseSplit(phaseSplit, phaseSelectionType, phaseSelectionListType) { + this.phaseSelectionType = phaseSelectionType; + this.phaseSelectionListType = phaseSelectionListType; + this.selectedPhaseSplit = phaseSplit; + this.phaseName = phaseSplit.challenge_phase_name; + this.splitName = phaseSplit.dataset_split_name; + this.phaseVisibility = phaseSplit.showPrivate; + this.phaseSplitSelected(phaseSplit); + } + /** * Get 12Hour formatted date function. */ diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 5e262dd8a..9e5dde054 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -42,10 +42,10 @@ export class ApiService { * @param path path of API call. * @param isJson set to false when fetching some non-JSON content. */ - getUrl(path: string, isJson = true) { + getUrl(path: string, isJson = true, isLoader = true) { if (isJson) { this.prepareHttpOptions(); - return this.loadingWrapper(this.http.get(this.API + path, this.HTTP_OPTIONS)); + return this.loadingWrapper(this.http.get(this.API + path, this.HTTP_OPTIONS), isLoader); } else { this.prepareHttpOptions(true); const TEMP = Object.assign({}, this.HTTP_OPTIONS, { observe: 'response', responseType: 'text' }); @@ -117,9 +117,11 @@ export class ApiService { * @param path path of API call. * @param body stringified json body. */ - loadingWrapper(httpCall) { + loadingWrapper(httpCall, isLoader = true) { const SELF = this; - setTimeout(() => {this.globalService.toggleLoading(true); }, 100); + if (isLoader) { + setTimeout(() => {this.globalService.toggleLoading(true); }, 100); + } let success = (params) => {}; let error = (params) => {}; let final = () => {}; diff --git a/src/styles/base.scss b/src/styles/base.scss index e311296cb..718f58c26 100644 --- a/src/styles/base.scss +++ b/src/styles/base.scss @@ -38,6 +38,12 @@ body { margin: 0px; } +.result-wrn { + margin-top: 15px; + margin-bottom: 20px; +} + + /* Margins */ .ev-mt-50 { @@ -328,7 +334,32 @@ mat-chip { } + +/* Refresh list page */ + +.update-page { + display: block; + background: $dark-gray; + overflow: auto; + padding: 10px; + text-align: center; + color: #fff; + position: absolute; + width: auto; + right: 0; + padding: 10px 30px; + box-shadow: 2px 2px 9px #6d6d6d; + border-radius: 3px 0px 0px 3px; + font-weight: 300; + transition: all 0.2s ease-in-out; + &:hover { + box-shadow: -1px 0px 9px #bbbbbb; + } +} + + /**File field styles*/ + .file-field { position: relative; } @@ -695,6 +726,7 @@ input[type=checkbox].filled-in:not(:checked)+label:after { /* radio button styles */ + [type=radio]:not(:checked), [type=radio]:checked { position: absolute; left: -9999px;