public async Task <ActionResult> UpdateMatch([Bind(Prefix = "CurrentInnings", Include = "MatchInnings,PlayerInningsSearch")] MatchInningsViewModel postedData)
        {
            if (postedData is null)
            {
                throw new ArgumentNullException(nameof(postedData));
            }

            var beforeUpdate = await _matchDataSource.ReadMatchByRoute(Request.RawUrl).ConfigureAwait(false);

            if (beforeUpdate.StartTime > DateTime.UtcNow || beforeUpdate.Tournament != null)
            {
                return(new HttpNotFoundResult());
            }

            if (beforeUpdate.MatchResultType.HasValue && new List <MatchResultType> {
                MatchResultType.HomeWinByForfeit, MatchResultType.AwayWinByForfeit, MatchResultType.Postponed, MatchResultType.Cancelled
            }.Contains(beforeUpdate.MatchResultType.Value))
            {
                return(new HttpNotFoundResult());
            }

            var i = 0;

            foreach (var innings in postedData.PlayerInningsSearch)
            {
                // The batter name is required if any other fields are filled in for an innings
                if (string.IsNullOrWhiteSpace(innings.Batter) &&
                    (innings.DismissalType.HasValue &&
                     innings.DismissalType != DismissalType.DidNotBat ||
                     !string.IsNullOrWhiteSpace(innings.DismissedBy) ||
                     !string.IsNullOrWhiteSpace(innings.Bowler) ||
                     innings.RunsScored != null ||
                     innings.BallsFaced != null))
                {
                    ModelState.AddModelError($"CurrentInnings.PlayerInningsSearch[{i}].Batter", $"You've added details for the {(i + 1).Ordinalize(CultureInfo.CurrentCulture)} batter. Please name the batter.");
                }

                // The batter must have batted if any other fields are filled in for an innings
                if ((innings.DismissalType == DismissalType.DidNotBat || innings.DismissalType == DismissalType.TimedOut) &&
                    (!string.IsNullOrWhiteSpace(innings.DismissedBy) ||
                     !string.IsNullOrWhiteSpace(innings.Bowler) ||
                     innings.RunsScored != null ||
                     innings.BallsFaced != null))
                {
                    ModelState.AddModelError($"CurrentInnings.PlayerInningsSearch[{i}].DismissalType", $"You've said the {(i + 1).Ordinalize(CultureInfo.CurrentCulture)} batter did not bat, but you added batting details.");
                }

                // The batter can't be not out if a a bowler or fielder is named
                if ((innings.DismissalType == DismissalType.NotOut || innings.DismissalType == DismissalType.Retired || innings.DismissalType == DismissalType.RetiredHurt) &&
                    (!string.IsNullOrWhiteSpace(innings.DismissedBy) ||
                     !string.IsNullOrWhiteSpace(innings.Bowler)
                    ))
                {
                    ModelState.AddModelError($"CurrentInnings.PlayerInningsSearch[{i}].DismissalType", $"You've said the {(i + 1).Ordinalize(CultureInfo.CurrentCulture)} batter was not out, but you named a fielder and/or bowler.");
                }

                // Caught and bowled by the same person is caught and bowled
                if (innings.DismissalType == DismissalType.Caught &&
                    !string.IsNullOrWhiteSpace(innings.DismissedBy) &&
                    innings.DismissedBy?.Trim() == innings.Bowler?.Trim())
                {
                    innings.DismissalType = DismissalType.CaughtAndBowled;
                    innings.DismissedBy   = null;
                }

                // If there's a fielder, the dismissal type should be caught or run-out
                if (innings.DismissalType != DismissalType.Caught &&
                    innings.DismissalType != DismissalType.RunOut &&
                    !string.IsNullOrWhiteSpace(innings.DismissedBy))
                {
                    ModelState.AddModelError($"CurrentInnings.PlayerInningsSearch[{i}].DismissalType", $"You've named the fielder for the {(i + 1).Ordinalize(CultureInfo.CurrentCulture)} batter, but they were not caught or run-out.");
                }

                i++;
            }

            var model = new EditScorecardViewModel(CurrentPage, Services.UserService)
            {
                Match = beforeUpdate,
                InningsOrderInMatch = _matchInningsUrlParser.ParseInningsOrderInMatchFromUrl(new Uri(Request.RawUrl, UriKind.Relative)),
                DateFormatter       = _dateTimeFormatter,
                Autofocus           = true
            };

            model.CurrentInnings.MatchInnings = model.Match.MatchInnings.Single(x => x.InningsOrderInMatch == model.InningsOrderInMatch);
            model.CurrentInnings.MatchInnings.PlayerInnings = postedData.PlayerInningsSearch.Where(x => !string.IsNullOrWhiteSpace(x.Batter)).Select(x => new PlayerInnings
            {
                Batter = new PlayerIdentity
                {
                    PlayerIdentityName = x.Batter.Trim(),
                    Team = model.CurrentInnings.MatchInnings.BattingTeam.Team
                },
                DismissalType = x.DismissalType,
                DismissedBy   = string.IsNullOrWhiteSpace(x.DismissedBy) ? null : new PlayerIdentity
                {
                    PlayerIdentityName = x.DismissedBy.Trim(),
                    Team = model.CurrentInnings.MatchInnings.BowlingTeam.Team
                },
                Bowler = string.IsNullOrWhiteSpace(x.Bowler) ? null : new PlayerIdentity
                {
                    PlayerIdentityName = x.Bowler.Trim(),
                    Team = model.CurrentInnings.MatchInnings.BowlingTeam.Team
                },
                RunsScored = x.RunsScored,
                BallsFaced = x.BallsFaced
            }).ToList();
            model.CurrentInnings.PlayerInningsSearch             = postedData.PlayerInningsSearch;
            model.CurrentInnings.MatchInnings.Byes               = postedData.MatchInnings.Byes;
            model.CurrentInnings.MatchInnings.Wides              = postedData.MatchInnings.Wides;
            model.CurrentInnings.MatchInnings.NoBalls            = postedData.MatchInnings.NoBalls;
            model.CurrentInnings.MatchInnings.BonusOrPenaltyRuns = postedData.MatchInnings.BonusOrPenaltyRuns;
            model.CurrentInnings.MatchInnings.Runs               = postedData.MatchInnings.Runs;
            model.CurrentInnings.MatchInnings.Wickets            = postedData.MatchInnings.Wickets;


            if (!model.Match.PlayersPerTeam.HasValue)
            {
                model.Match.PlayersPerTeam = model.Match.Tournament != null ? 8 : 11;
            }
            if (model.Match.PlayersPerTeam.Value < postedData.MatchInnings.PlayerInnings.Count)
            {
                model.Match.PlayersPerTeam = postedData.MatchInnings.PlayerInnings.Count;
            }
            _playerInningsScaffolder.ScaffoldPlayerInnings(model.CurrentInnings.MatchInnings.PlayerInnings, model.Match.PlayersPerTeam.Value);

            model.CurrentInnings.MatchInnings.BowlingFigures = _bowlingFiguresCalculator.CalculateBowlingFigures(model.CurrentInnings.MatchInnings);

            model.IsAuthorized = _authorizationPolicy.IsAuthorized(beforeUpdate);

            if (model.IsAuthorized[AuthorizedAction.EditMatchResult] && ModelState.IsValid)
            {
                var currentMember = Members.GetCurrentMember();
                await _matchRepository.UpdateBattingScorecard(model.Match, model.CurrentInnings.MatchInnings.MatchInningsId.Value, currentMember.Key, currentMember.Name).ConfigureAwait(false);

                // redirect to the bowling scorecard for this innings
                return(Redirect($"{model.Match.MatchRoute}/edit/innings/{model.InningsOrderInMatch.Value}/bowling"));
            }

            model.Metadata.PageTitle = "Edit " + model.Match.MatchFullName(x => _dateTimeFormatter.FormatDate(x, false, false, false));

            while (model.CurrentInnings.MatchInnings.OversBowled.Count < model.CurrentInnings.MatchInnings.OverSets.Sum(x => x.Overs))
            {
                model.CurrentInnings.MatchInnings.OversBowled.Add(new Over());
            }

            if (model.Match.Season != null)
            {
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = Constants.Pages.Competitions, Url = new Uri(Constants.Pages.CompetitionsUrl, UriKind.Relative)
                });
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = model.Match.Season.Competition.CompetitionName, Url = new Uri(model.Match.Season.Competition.CompetitionRoute, UriKind.Relative)
                });
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = model.Match.Season.SeasonName(), Url = new Uri(model.Match.Season.SeasonRoute, UriKind.Relative)
                });
            }
            else
            {
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = Constants.Pages.Matches, Url = new Uri(Constants.Pages.MatchesUrl, UriKind.Relative)
                });
            }
            model.Breadcrumbs.Add(new Breadcrumb {
                Name = model.Match.MatchName, Url = new Uri(model.Match.MatchRoute, UriKind.Relative)
            });

            return(View("EditBattingScorecard", model));
        }
        public async override Task <ActionResult> Index(ContentModel contentModel)
        {
            if (contentModel is null)
            {
                throw new ArgumentNullException(nameof(contentModel));
            }

            var model = new EditScorecardViewModel(contentModel.Content, Services?.UserService)
            {
                Match = await _matchDataSource.ReadMatchByRoute(Request.RawUrl).ConfigureAwait(false),
                InningsOrderInMatch = _matchInningsUrlParser.ParseInningsOrderInMatchFromUrl(new Uri(Request.RawUrl, UriKind.Relative)),
                DateFormatter       = _dateFormatter,
                Autofocus           = true
            };

            if (model.Match == null || !model.InningsOrderInMatch.HasValue || model.Match.Tournament != null)
            {
                return(new HttpNotFoundResult());
            }
            else
            {
                // This page is only for matches in the past
                if (model.Match.StartTime > DateTime.UtcNow)
                {
                    return(new HttpNotFoundResult());
                }

                // This page is not for matches not played
                if (model.Match.MatchResultType.HasValue && new List <MatchResultType> {
                    MatchResultType.HomeWinByForfeit, MatchResultType.AwayWinByForfeit, MatchResultType.Postponed, MatchResultType.Cancelled
                }.Contains(model.Match.MatchResultType.Value))
                {
                    return(new HttpNotFoundResult());
                }

                model.IsAuthorized = _authorizationPolicy.IsAuthorized(model.Match);

                model.CurrentInnings.MatchInnings = model.Match.MatchInnings.Single(x => x.InningsOrderInMatch == model.InningsOrderInMatch);
                if (!model.CurrentInnings.MatchInnings.OverSets.Any())
                {
                    model.CurrentInnings.MatchInnings.OverSets.Add(new OverSet {
                        Overs = model.Match.Tournament != null ? 6 : 12, BallsPerOver = 8
                    });
                }
                while (model.CurrentInnings.MatchInnings.OversBowled.Count < model.CurrentInnings.MatchInnings.OverSets.Sum(x => x.Overs))
                {
                    model.CurrentInnings.MatchInnings.OversBowled.Add(new Over());
                }

                // Convert overs bowled to a view model, purely to change the field names to ones which will not trigger pop-up contact/password managers
                // while retaining the benefits of ASP.NET model binding. Using the "search" keyword in the property name also helps to disable contact/password managers.
                model.CurrentInnings.OversBowledSearch.AddRange(model.CurrentInnings.MatchInnings.OversBowled.Select(x => new OverViewModel
                {
                    BowledBy     = x.Bowler?.PlayerIdentityName,
                    BallsBowled  = x.BallsBowled,
                    Wides        = x.Wides,
                    NoBalls      = x.NoBalls,
                    RunsConceded = x.RunsConceded
                }));

                model.Metadata.PageTitle = "Edit " + model.Match.MatchFullName(x => _dateFormatter.FormatDate(x, false, false, false));

                if (model.Match.Season != null)
                {
                    model.Breadcrumbs.Add(new Breadcrumb {
                        Name = Constants.Pages.Competitions, Url = new Uri(Constants.Pages.CompetitionsUrl, UriKind.Relative)
                    });
                    model.Breadcrumbs.Add(new Breadcrumb {
                        Name = model.Match.Season.Competition.CompetitionName, Url = new Uri(model.Match.Season.Competition.CompetitionRoute, UriKind.Relative)
                    });
                    model.Breadcrumbs.Add(new Breadcrumb {
                        Name = model.Match.Season.SeasonName(), Url = new Uri(model.Match.Season.SeasonRoute, UriKind.Relative)
                    });
                }
                else
                {
                    model.Breadcrumbs.Add(new Breadcrumb {
                        Name = Constants.Pages.Matches, Url = new Uri(Constants.Pages.MatchesUrl, UriKind.Relative)
                    });
                }
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = model.Match.MatchName, Url = new Uri(model.Match.MatchRoute, UriKind.Relative)
                });

                return(CurrentTemplate(model));
            }
        }
Exemple #3
0
        public async Task <ActionResult> UpdateMatch([Bind(Prefix = "CurrentInnings", Include = "MatchInnings,OversBowledSearch")] MatchInningsViewModel postedData)
        {
            if (postedData is null)
            {
                throw new ArgumentNullException(nameof(postedData));
            }

            var beforeUpdate = await _matchDataSource.ReadMatchByRoute(Request.RawUrl).ConfigureAwait(false);

            if (beforeUpdate.StartTime > DateTime.UtcNow || beforeUpdate.Tournament != null)
            {
                return(new HttpNotFoundResult());
            }

            if (beforeUpdate.MatchResultType.HasValue && new List <MatchResultType> {
                MatchResultType.HomeWinByForfeit, MatchResultType.AwayWinByForfeit, MatchResultType.Postponed, MatchResultType.Cancelled
            }.Contains(beforeUpdate.MatchResultType.Value))
            {
                return(new HttpNotFoundResult());
            }

            // The bowler name is required if any other fields are filled in for an over
            var i = 0;

            foreach (var over in postedData.OversBowledSearch)
            {
                if (string.IsNullOrWhiteSpace(over.BowledBy) &&
                    (over.BallsBowled.HasValue ||
                     over.Wides.HasValue ||
                     over.NoBalls.HasValue ||
                     over.RunsConceded.HasValue))
                {
                    ModelState.AddModelError($"CurrentInnings.OversBowledSearch[{i}].Bowler", $"You've added the {(i + 1).Ordinalize(CultureInfo.CurrentCulture)} over. Please name the bowler.");
                }
                i++;
            }

            var model = new EditScorecardViewModel(CurrentPage, Services.UserService)
            {
                Match = beforeUpdate,
                InningsOrderInMatch = _matchInningsUrlParser.ParseInningsOrderInMatchFromUrl(new Uri(Request.RawUrl, UriKind.Relative)),
                DateFormatter       = _dateTimeFormatter,
                Autofocus           = true
            };

            model.CurrentInnings.MatchInnings             = model.Match.MatchInnings.Single(x => x.InningsOrderInMatch == model.InningsOrderInMatch);
            model.CurrentInnings.MatchInnings.OversBowled = postedData.OversBowledSearch.Where(x => x.BowledBy?.Trim().Length > 0).Select((x, index) => new Over
            {
                Bowler = new PlayerIdentity
                {
                    PlayerIdentityName = x.BowledBy.Trim(),
                    Team = model.CurrentInnings.MatchInnings.BowlingTeam.Team
                },
                OverNumber   = index + 1,
                BallsBowled  = x.BallsBowled,
                Wides        = x.Wides,
                NoBalls      = x.NoBalls,
                RunsConceded = x.RunsConceded
            }).ToList();

            model.CurrentInnings.OversBowledSearch = postedData.OversBowledSearch;

            _overSetScaffolder.ScaffoldOverSets(model.CurrentInnings.MatchInnings.OverSets, model.Match.Tournament != null ? 6 : 12, model.CurrentInnings.MatchInnings.OversBowled);

            model.CurrentInnings.MatchInnings.BowlingFigures = _bowlingFiguresCalculator.CalculateBowlingFigures(model.CurrentInnings.MatchInnings);

            model.IsAuthorized = _authorizationPolicy.IsAuthorized(beforeUpdate);

            if (model.IsAuthorized[AuthorizedAction.EditMatchResult] && ModelState.IsValid)
            {
                var currentMember = Members.GetCurrentMember();
                await _matchRepository.UpdateBowlingScorecard(model.Match, model.CurrentInnings.MatchInnings.MatchInningsId.Value, currentMember.Key, currentMember.Name).ConfigureAwait(false);

                // redirect to the next innings or close of play
                if (model.InningsOrderInMatch.Value < model.Match.MatchInnings.Count)
                {
                    return(Redirect($"{model.Match.MatchRoute}/edit/innings/{model.InningsOrderInMatch.Value + 1}/batting"));
                }
                else
                {
                    return(Redirect(model.Match.MatchRoute + "/edit/close-of-play"));
                }
            }

            model.Metadata.PageTitle = "Edit " + model.Match.MatchFullName(x => _dateTimeFormatter.FormatDate(x, false, false, false));

            while (model.CurrentInnings.MatchInnings.OversBowled.Count < model.CurrentInnings.MatchInnings.OverSets.Sum(x => x.Overs))
            {
                model.CurrentInnings.MatchInnings.OversBowled.Add(new Over());
            }

            if (model.Match.Season != null)
            {
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = Constants.Pages.Competitions, Url = new Uri(Constants.Pages.CompetitionsUrl, UriKind.Relative)
                });
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = model.Match.Season.Competition.CompetitionName, Url = new Uri(model.Match.Season.Competition.CompetitionRoute, UriKind.Relative)
                });
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = model.Match.Season.SeasonName(), Url = new Uri(model.Match.Season.SeasonRoute, UriKind.Relative)
                });
            }
            else
            {
                model.Breadcrumbs.Add(new Breadcrumb {
                    Name = Constants.Pages.Matches, Url = new Uri(Constants.Pages.MatchesUrl, UriKind.Relative)
                });
            }
            model.Breadcrumbs.Add(new Breadcrumb {
                Name = model.Match.MatchName, Url = new Uri(model.Match.MatchRoute, UriKind.Relative)
            });

            return(View("EditBowlingScorecard", model));
        }