public void TournamentAggregate_ProduceResult_ResultIsProduced() { TournamentAggregate tournamentAggregate = TournamentTestData.GetCompletedTournamentAggregateWithCSSCalculatedAggregate(20, 15, 23, 16, 0, 5); tournamentAggregate.ProduceResult(); tournamentAggregate.HasResultBeenProduced.ShouldBeTrue(); List <PlayerScoreRecordDataTransferObject> scores = tournamentAggregate.GetScores(); scores.Any(s => s.Last9HolesScore == 0).ShouldBeFalse(); scores.Any(s => s.Last6HolesScore == 0).ShouldBeFalse(); scores.Any(s => s.Last3HolesScore == 0).ShouldBeFalse(); scores.Any(s => s.TournamentDivision == 0).ShouldBeFalse(); scores.Any(s => s.Position == 0).ShouldBeFalse(); scores.Any(s => s.MeasuredCourseId == Guid.Empty).ShouldBeFalse(); scores.Any(s => s.PlayerId == Guid.Empty).ShouldBeFalse(); scores.Any(s => s.NetScore == 0).ShouldBeFalse(); scores.Any(s => s.GrossScore == 0).ShouldBeFalse(); scores.Any(s => s.PlayingHandicap < 0).ShouldBeFalse(); scores.Any(s => s.PlayingHandicap > 36).ShouldBeFalse(); scores.Any(s => s.TournamentId == Guid.Empty).ShouldBeFalse(); scores.Any(s => s.ScoreDate == DateTime.MinValue).ShouldBeFalse(); scores.Any(s => s.GolfClubId == Guid.Empty).ShouldBeFalse(); scores.Any(s => s.HandicapCategory == 0).ShouldBeFalse(); scores.Any(s => s.HoleScores.Count() != 18).ShouldBeFalse(); scores.Any(s => s.CSS == 0).ShouldBeFalse(); }
public void TournamentAggregate_GetScores_ScoresReturned() { TournamentAggregate aggregate = TournamentTestData.GetCreatedTournamentWithScoresRecordedAggregate(); List <PlayerScoreRecordDataTransferObject> scores = aggregate.GetScores(); scores.ShouldNotBeNull(); scores.Count.ShouldBe(1); }
public async Task <IActionResult> GetTournament([FromRoute] Guid tournamentId, [FromQuery] Boolean includeScores, CancellationToken cancellationToken) { TournamentAggregate tournament = await this.TournamentRepository.GetLatestVersion(tournamentId, cancellationToken); if (!tournament.HasBeenCreated) { return(this.NotFound($"Tournament not found with Id {tournamentId}")); } Developer.DataTransferObjects.GetTournamentResponse response = new Developer.DataTransferObjects.GetTournamentResponse { HasBeenCreated = tournament.HasBeenCreated, Name = tournament.Name, GolfClubId = tournament.GolfClubId, Adjustment = tournament.Adjustment, CSS = tournament.CSS, CSSHasBeenCalculated = tournament.CSSHasBeenCalculated, CancelledDateTime = tournament.CancelledDateTime, CancelledReason = tournament.CancelledReason, CompletedDateTime = tournament.CompletedDateTime, Format = tournament.Format.ConvertTo <TournamentFormat>(), HasBeenCancelled = tournament.HasBeenCancelled, HasBeenCompleted = tournament.HasBeenCompleted, MeasuredCourseId = tournament.MeasuredCourseId, MeasuredCourseSSS = tournament.MeasuredCourseSSS, MemberCategory = tournament.PlayerCategory.ConvertTo <PlayerCategory>(), TournamentDate = tournament.TournamentDate }; if (includeScores) { List <PlayerScoreRecordDataTransferObject> scores = tournament.GetScores(); if (scores.Count > 0) { scores.ForEach(s => { response.Scores.Add(new MemberScoreRecord { GrossScore = s.GrossScore, PlayingHandicap = s.PlayingHandicap, HandicapCategory = s.HandicapCategory, HoleScores = s.HoleScores, MemberId = s.PlayerId, NetScore = s.NetScore }); }); } } return(this.Ok(response)); }
public void TournamentAggregate_CalculateCSS_CSSCalculated(Int32 category1Scores, Int32 category2Scores, Int32 category3Scores, Int32 category4Scores, Int32 category5Scores, Int32 bufferorbetter, Int32 expectedAdjustment, Int32 expectedCSS) { TournamentAggregate aggregate = TournamentTestData.GetCompletedTournamentAggregate(category1Scores, category2Scores, category3Scores, category4Scores, category5Scores, bufferorbetter); aggregate.CalculateCSS(); aggregate.Adjustment.ShouldBe(expectedAdjustment); aggregate.CSS.ShouldBe(expectedCSS); aggregate.GetScores().All(s => s.IsPublished).ShouldBeTrue(); }
/// <summary> /// Starts the handicap calculation process. /// </summary> /// <param name="tournament">The tournament.</param> /// <param name="startedDateTime">The started date time.</param> /// <exception cref="InvalidOperationException">All Tournament scores must be published to run a handicap calculation process /// or /// Tournament must be completed to run a handicap calculation process</exception> public void StartHandicapCalculationProcess(TournamentAggregate tournament, DateTime startedDateTime) { // TODO: Business rules to protect reruns etc // TODO: handle resuming errored processes this.EnsureProcessCanBeStarted(); if (!tournament.HasBeenCompleted) { throw new InvalidOperationException("Tournament must be completed to run a handicap calculation process"); } if (!tournament.GetScores().All(s => s.IsPublished)) { throw new InvalidOperationException("All Tournament scores must be published to run a handicap calculation process"); } HandicapCalculationProcessStartedEvent handicapCalculationProcessStartedEvent = HandicapCalculationProcessStartedEvent.Create(this.AggregateId, startedDateTime); this.ApplyAndPend(handicapCalculationProcessStartedEvent); }
/// <summary> /// Runs the specified process identifier. /// </summary> /// <param name="processId">The process identifier.</param> /// <param name="tournamentId">The tournament identifier.</param> /// <param name="processRunDateTime">The process run date time.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> private async Task Run(Guid processId, Guid tournamentId, DateTime processRunDateTime, CancellationToken cancellationToken) { Boolean processedWithoutError = true; String processedErrorMessage = String.Empty; // Mark the aggregate as 'Running' await this.UpdateHandicapCalculationStatus(processId, tournamentId, HandicapCalculationStatus.Running, cancellationToken); List <PlayerScoreRecordDataTransferObject> publishedScores = new List <PlayerScoreRecordDataTransferObject>(); try { // Rehydrate the tournament TournamentAggregate tournamentAggregate = await this.TournamentRepository.GetLatestVersion(tournamentId, cancellationToken); // Get the published scores publishedScores = tournamentAggregate.GetScores().Where(s => s.IsPublished).ToList(); Int32 maxDegreeOfParallelism = 15; ActionBlock <PlayerScoreRecordDataTransferObject> workerBlock = new ActionBlock <PlayerScoreRecordDataTransferObject>(async score => await this.ApplyHandicapAdjustment(score, cancellationToken), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism, CancellationToken = cancellationToken, BoundedCapacity = 20, }); foreach (PlayerScoreRecordDataTransferObject publishedScore in publishedScores) { while (workerBlock.InputCount > 20) { Thread.Sleep(TimeSpan.FromSeconds(2)); } workerBlock.Post(publishedScore); } workerBlock.Complete(); await workerBlock.Completion; } catch (Exception ex) { Logger.LogCritical(ex); processedWithoutError = false; processedErrorMessage = ex.Message; } if (processedWithoutError) { Int32 scoreCount = publishedScores.Any() ? publishedScores.Count : 0; // update aggregate with 'Completed' event. await this.UpdateHandicapCalculationStatus(processId, tournamentId, HandicapCalculationStatus.Complete, cancellationToken); Logger.LogInformation($"Process finished, {scoreCount} scores processed."); } else { // update aggregate with 'Errored' event. await this.UpdateHandicapCalculationStatus(processId, tournamentId, HandicapCalculationStatus.Error, cancellationToken, processedErrorMessage); Logger.LogError(new Exception("Process finished with error.")); } }