SKE-21 display competition ranking
This commit is contained in:
parent
180830c921
commit
537f08e99c
@ -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;
|
||||||
}
|
}
|
@ -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>
|
||||||
|
@ -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';
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user