SKE-21 display competition ranking
This commit is contained in:
parent
180830c921
commit
537f08e99c
@ -12,6 +12,7 @@ hr {
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.section-color {
|
||||
@ -44,3 +45,7 @@ hr {
|
||||
margin: 4px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 0px;
|
||||
}
|
@ -7,6 +7,29 @@
|
||||
<div class="description">
|
||||
<div class="section-color">Nagrody:</div> {{competition.prize}}
|
||||
</div>
|
||||
<div *ngIf="mode!=='quiz'" class="card">
|
||||
<div class="header">
|
||||
<h2>Ranking konkursu</h2>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover dashboard-task-infos">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Użytkownik</th>
|
||||
<th>Punkty</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let position of ranking">
|
||||
<td>{{position.participantName}}</td>
|
||||
<td>{{position.points}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" style="margin-top: 20px" [disabled]="mode==='quiz' || !canSolveCompetition" class="btn btn-success" (click)="solveCompetition()">
|
||||
{{l("Start")}}
|
||||
</button>
|
||||
|
@ -6,11 +6,13 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import { List } from 'lodash';
|
||||
import { CompetitionServiceProxy,
|
||||
CompetitionDto,
|
||||
QuestionDto,
|
||||
QuestionOptionDto,
|
||||
CreateRankingPositionDto } from '@shared/service-proxies/service-proxies';
|
||||
CreateRankingPositionDto,
|
||||
RankingPositionDto } from '@shared/service-proxies/service-proxies';
|
||||
|
||||
@Component({
|
||||
templateUrl: './competition-detail.component.html',
|
||||
@ -25,6 +27,7 @@ export class CompetitionDetailComponent extends AppComponentBase implements OnIn
|
||||
|
||||
public competitionId: number;
|
||||
public competition: CompetitionDto = null;
|
||||
public ranking: List<RankingPositionDto> = [];
|
||||
public competitionDetailAreaId: string = 'competition-detail-area';
|
||||
|
||||
public canSolveCompetition: boolean = false;
|
||||
@ -61,9 +64,10 @@ export class CompetitionDetailComponent extends AppComponentBase implements OnIn
|
||||
this.setBusy(this.competitionDetailAreaId);
|
||||
|
||||
this.paramSubscription = forkJoin([this._competitionService.getCompetition(this.competitionId),
|
||||
this._competitionService.canSolveCompetition(this.competitionId)])
|
||||
this._competitionService.canSolveCompetition(this.competitionId),
|
||||
this._competitionService.getRanking(this.competitionId)])
|
||||
.pipe(finalize(() => { this.clearBusy(this.competitionDetailAreaId); }))
|
||||
.subscribe((data: [CompetitionDto, boolean]) => {
|
||||
.subscribe((data: [CompetitionDto, boolean, List<RankingPositionDto>]) => {
|
||||
this.competition = data[0];
|
||||
this.competition.questions.forEach((x) => this.shuffleOptions(x.questionOptions));
|
||||
this.pager.count = this.competition.questions.length;
|
||||
@ -71,6 +75,9 @@ export class CompetitionDetailComponent extends AppComponentBase implements OnIn
|
||||
|
||||
this.canSolveCompetition = data[1];
|
||||
console.log(this.canSolveCompetition);
|
||||
|
||||
this.ranking = data[2];
|
||||
console.log(this.ranking);
|
||||
});
|
||||
}
|
||||
|
||||
@ -135,10 +142,12 @@ export class CompetitionDetailComponent extends AppComponentBase implements OnIn
|
||||
this.solveSubscription = this._competitionService.solveCompetition(rankingPosition)
|
||||
.pipe(mergeMap((result: number) => {
|
||||
const solveCompetitionStream = this._competitionService.canSolveCompetition(this.competitionId);
|
||||
return solveCompetitionStream.pipe(finalize(() => { this.clearBusy(this.competitionDetailAreaId); }));
|
||||
const rankingStream = this._competitionService.getRanking(this.competitionId);
|
||||
return forkJoin([solveCompetitionStream, rankingStream]).pipe(finalize(() => { this.clearBusy(this.competitionDetailAreaId); }));
|
||||
}))
|
||||
.subscribe((data: boolean) => {
|
||||
this.canSolveCompetition = data;
|
||||
.subscribe((data: [boolean, List<RankingPositionDto>]) => {
|
||||
this.canSolveCompetition = data[0];
|
||||
this.ranking = data[1];
|
||||
});
|
||||
|
||||
this.mode = 'result';
|
||||
|
@ -385,6 +385,65 @@ export class CompetitionServiceProxy {
|
||||
return _observableOf<number>(<any>null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @competitionId (optional)
|
||||
* @return Success
|
||||
*/
|
||||
getRanking(competitionId: number | null | undefined): Observable<RankingPositionDto[]> {
|
||||
let url_ = this.baseUrl + "/api/services/app/Competition/GetRanking?";
|
||||
if (competitionId !== undefined)
|
||||
url_ += "competitionId=" + encodeURIComponent("" + competitionId) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_ : any = {
|
||||
observe: "response",
|
||||
responseType: "blob",
|
||||
headers: new HttpHeaders({
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
})
|
||||
};
|
||||
|
||||
return this.http.request("get", url_, options_).pipe(_observableMergeMap((response_ : any) => {
|
||||
return this.processGetRanking(response_);
|
||||
})).pipe(_observableCatch((response_: any) => {
|
||||
if (response_ instanceof HttpResponseBase) {
|
||||
try {
|
||||
return this.processGetRanking(<any>response_);
|
||||
} catch (e) {
|
||||
return <Observable<RankingPositionDto[]>><any>_observableThrow(e);
|
||||
}
|
||||
} else
|
||||
return <Observable<RankingPositionDto[]>><any>_observableThrow(response_);
|
||||
}));
|
||||
}
|
||||
|
||||
protected processGetRanking(response: HttpResponseBase): Observable<RankingPositionDto[]> {
|
||||
const status = response.status;
|
||||
const responseBlob =
|
||||
response instanceof HttpResponse ? response.body :
|
||||
(<any>response).error instanceof Blob ? (<any>response).error : undefined;
|
||||
|
||||
let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }};
|
||||
if (status === 200) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
if (resultData200 && resultData200.constructor === Array) {
|
||||
result200 = [];
|
||||
for (let item of resultData200)
|
||||
result200.push(RankingPositionDto.fromJS(item));
|
||||
}
|
||||
return _observableOf(result200);
|
||||
}));
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
}));
|
||||
}
|
||||
return _observableOf<RankingPositionDto[]>(<any>null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @competitionId (optional)
|
||||
* @return Success
|
||||
@ -2611,6 +2670,61 @@ export interface ICreateRankingPositionDto {
|
||||
points: number | undefined;
|
||||
}
|
||||
|
||||
export class RankingPositionDto implements IRankingPositionDto {
|
||||
participantName: number | undefined;
|
||||
competitionId: number | undefined;
|
||||
points: number | undefined;
|
||||
id: number | undefined;
|
||||
|
||||
constructor(data?: IRankingPositionDto) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(data?: any) {
|
||||
if (data) {
|
||||
this.participantName = data["participantName"];
|
||||
this.competitionId = data["competitionId"];
|
||||
this.points = data["points"];
|
||||
this.id = data["id"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): RankingPositionDto {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new RankingPositionDto();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["participantName"] = this.participantName;
|
||||
data["competitionId"] = this.competitionId;
|
||||
data["points"] = this.points;
|
||||
data["id"] = this.id;
|
||||
return data;
|
||||
}
|
||||
|
||||
clone(): RankingPositionDto {
|
||||
const json = this.toJSON();
|
||||
let result = new RankingPositionDto();
|
||||
result.init(json);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRankingPositionDto {
|
||||
participantName: number | undefined;
|
||||
competitionId: number | undefined;
|
||||
points: number | undefined;
|
||||
id: number | undefined;
|
||||
}
|
||||
|
||||
export class ChangeUiThemeInput implements IChangeUiThemeInput {
|
||||
theme: string;
|
||||
|
||||
|
@ -140,6 +140,19 @@ namespace SystemKonkursow.Competition.CompetitionCategory
|
||||
return newRankingPositionId;
|
||||
}
|
||||
|
||||
[AbpAuthorize]
|
||||
public List<RankingPositionDto> GetRanking(int competitionId)
|
||||
{
|
||||
var ranking = _rankingPositionRepository.GetAll()
|
||||
.Include(t => t.User)
|
||||
.Where(t => t.CompetitionId == competitionId).ToList();
|
||||
|
||||
var mappedObjects = ObjectMapper.Map<List<RankingPositionDto>>(ranking
|
||||
.OrderByDescending(t => t.Points));
|
||||
|
||||
return mappedObjects;
|
||||
}
|
||||
|
||||
[AbpAuthorize]
|
||||
public async Task<bool> CanSolveCompetition(int competitionId)
|
||||
{
|
||||
@ -160,13 +173,18 @@ namespace SystemKonkursow.Competition.CompetitionCategory
|
||||
return false;
|
||||
}
|
||||
|
||||
var rankingPosition = await _rankingPositionRepository.GetAll().Where(t => t.UserId == user.Id).FirstOrDefaultAsync();
|
||||
var rankingPosition = await _rankingPositionRepository.GetAll().Where(t => t.UserId == user.Id && t.CompetitionId == competitionId).FirstOrDefaultAsync();
|
||||
|
||||
if (null != rankingPosition)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DateHelper.IsBetween(competition.StartDate, competition.EndDate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ namespace SystemKonkursow.Competition.CompetitionCategory.Dto
|
||||
.ForMember(dest => dest.CreatorName, opt => opt.MapFrom(src => src.Creator.UserName));
|
||||
|
||||
CreateMap<CreateRankingPositionDto, Domain.RankingPosition>();
|
||||
|
||||
CreateMap<Domain.RankingPosition, RankingPositionDto>()
|
||||
.ForMember(dest => dest.ParticipantName, opt => opt.MapFrom(src => src.User.UserName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
using Abp.Application.Services.Dto;
|
||||
|
||||
namespace SystemKonkursow.Competition.CompetitionCategory.Dto
|
||||
{
|
||||
public class RankingPositionDto : EntityDto<int>
|
||||
{
|
||||
public string ParticipantName { get; set; }
|
||||
|
||||
public long CompetitionId { get; set; }
|
||||
|
||||
public int Points { get; set; }
|
||||
}
|
||||
}
|
@ -9,5 +9,13 @@ namespace SystemKonkursow
|
||||
TimeZoneInfo timeZone = TimeZoneInfo.Local;
|
||||
return TimeZoneInfo.ConvertTime(dateTime, timeZone);
|
||||
}
|
||||
|
||||
public static bool IsBetween(DateTime leftDate, DateTime rightDate)
|
||||
{
|
||||
DateTime today = DateTime.Today;
|
||||
|
||||
return (today >= leftDate && today <= rightDate);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user