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);
        }
コード例 #6
0
        /// <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."));
            }
        }