SKE-21 display competition ranking

This commit is contained in:
Przemysław Stawujak 2019-01-03 16:45:48 +01:00
parent 180830c921
commit 537f08e99c
8 changed files with 201 additions and 8 deletions

View File

@ -12,9 +12,10 @@ hr {
white-space: normal; white-space: normal;
word-wrap: break-word; word-wrap: break-word;
font-size: 16px; font-size: 16px;
margin-bottom: 10px;
} }
.section-color{ .section-color {
color: #771111; color: #771111;
} }
@ -43,4 +44,8 @@ hr {
background-color: #eee; background-color: #eee;
margin: 4px; margin: 4px;
padding: 6px; padding: 6px;
}
.card {
margin-bottom: 0px;
} }

View File

@ -7,6 +7,29 @@
<div class="description"> <div class="description">
<div class="section-color">Nagrody:</div> {{competition.prize}} <div class="section-color">Nagrody:</div> {{competition.prize}}
</div> </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()"> <button type="button" style="margin-top: 20px" [disabled]="mode==='quiz' || !canSolveCompetition" class="btn btn-success" (click)="solveCompetition()">
{{l("Start")}} {{l("Start")}}
</button> </button>

View File

@ -6,11 +6,13 @@ import { ActivatedRoute } from '@angular/router';
import { mergeMap } from 'rxjs/operators'; import { mergeMap } from 'rxjs/operators';
import { finalize } from 'rxjs/operators'; import { finalize } from 'rxjs/operators';
import { forkJoin } from 'rxjs'; import { forkJoin } from 'rxjs';
import { List } from 'lodash';
import { CompetitionServiceProxy, import { CompetitionServiceProxy,
CompetitionDto, CompetitionDto,
QuestionDto, QuestionDto,
QuestionOptionDto, QuestionOptionDto,
CreateRankingPositionDto } from '@shared/service-proxies/service-proxies'; CreateRankingPositionDto,
RankingPositionDto } from '@shared/service-proxies/service-proxies';
@Component({ @Component({
templateUrl: './competition-detail.component.html', templateUrl: './competition-detail.component.html',
@ -25,6 +27,7 @@ export class CompetitionDetailComponent extends AppComponentBase implements OnIn
public competitionId: number; public competitionId: number;
public competition: CompetitionDto = null; public competition: CompetitionDto = null;
public ranking: List<RankingPositionDto> = [];
public competitionDetailAreaId: string = 'competition-detail-area'; public competitionDetailAreaId: string = 'competition-detail-area';
public canSolveCompetition: boolean = false; public canSolveCompetition: boolean = false;
@ -61,9 +64,10 @@ export class CompetitionDetailComponent extends AppComponentBase implements OnIn
this.setBusy(this.competitionDetailAreaId); this.setBusy(this.competitionDetailAreaId);
this.paramSubscription = forkJoin([this._competitionService.getCompetition(this.competitionId), 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); })) .pipe(finalize(() => { this.clearBusy(this.competitionDetailAreaId); }))
.subscribe((data: [CompetitionDto, boolean]) => { .subscribe((data: [CompetitionDto, boolean, List<RankingPositionDto>]) => {
this.competition = data[0]; this.competition = data[0];
this.competition.questions.forEach((x) => this.shuffleOptions(x.questionOptions)); this.competition.questions.forEach((x) => this.shuffleOptions(x.questionOptions));
this.pager.count = this.competition.questions.length; this.pager.count = this.competition.questions.length;
@ -71,6 +75,9 @@ export class CompetitionDetailComponent extends AppComponentBase implements OnIn
this.canSolveCompetition = data[1]; this.canSolveCompetition = data[1];
console.log(this.canSolveCompetition); 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) this.solveSubscription = this._competitionService.solveCompetition(rankingPosition)
.pipe(mergeMap((result: number) => { .pipe(mergeMap((result: number) => {
const solveCompetitionStream = this._competitionService.canSolveCompetition(this.competitionId); 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) => { .subscribe((data: [boolean, List<RankingPositionDto>]) => {
this.canSolveCompetition = data; this.canSolveCompetition = data[0];
this.ranking = data[1];
}); });
this.mode = 'result'; this.mode = 'result';

View File

@ -385,6 +385,65 @@ export class CompetitionServiceProxy {
return _observableOf<number>(<any>null); 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) * @competitionId (optional)
* @return Success * @return Success
@ -2611,6 +2670,61 @@ export interface ICreateRankingPositionDto {
points: number | undefined; 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 { export class ChangeUiThemeInput implements IChangeUiThemeInput {
theme: string; theme: string;

View File

@ -140,6 +140,19 @@ namespace SystemKonkursow.Competition.CompetitionCategory
return newRankingPositionId; 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] [AbpAuthorize]
public async Task<bool> CanSolveCompetition(int competitionId) public async Task<bool> CanSolveCompetition(int competitionId)
{ {
@ -160,13 +173,18 @@ namespace SystemKonkursow.Competition.CompetitionCategory
return false; 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) if (null != rankingPosition)
{ {
return false; return false;
} }
if (!DateHelper.IsBetween(competition.StartDate, competition.EndDate))
{
return false;
}
return true; return true;
} }

View File

@ -24,6 +24,9 @@ namespace SystemKonkursow.Competition.CompetitionCategory.Dto
.ForMember(dest => dest.CreatorName, opt => opt.MapFrom(src => src.Creator.UserName)); .ForMember(dest => dest.CreatorName, opt => opt.MapFrom(src => src.Creator.UserName));
CreateMap<CreateRankingPositionDto, Domain.RankingPosition>(); CreateMap<CreateRankingPositionDto, Domain.RankingPosition>();
CreateMap<Domain.RankingPosition, RankingPositionDto>()
.ForMember(dest => dest.ParticipantName, opt => opt.MapFrom(src => src.User.UserName));
} }
} }
} }

View File

@ -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; }
}
}

View File

@ -9,5 +9,13 @@ namespace SystemKonkursow
TimeZoneInfo timeZone = TimeZoneInfo.Local; TimeZoneInfo timeZone = TimeZoneInfo.Local;
return TimeZoneInfo.ConvertTime(dateTime, timeZone); return TimeZoneInfo.ConvertTime(dateTime, timeZone);
} }
public static bool IsBetween(DateTime leftDate, DateTime rightDate)
{
DateTime today = DateTime.Today;
return (today >= leftDate && today <= rightDate);
}
} }
} }