public Round StartRoundOrGetExisting(int userId) { GameEntity gameEntity = StartNewGameOrGetExisting(userId); RoundEntity roundEntity = _roundRepository.GetCurrentOpenRound(gameEntity.Id); if (null != roundEntity) { // Unfinished round return(RoundEntityFactory.Create(roundEntity, ROUNDS_PER_GAME, RoundsPlayedInGame(gameEntity.Id))); } roundEntity = RoundEntityFactory.Create(gameEntity.Id); // Determine the correct one var randomImages = _imageRepository.GetRandomImages(IMAGES_PER_ROUND, _userRepository.Get(userId).FullName); int indexOfCorrectImageInList = (new Random()).Next(IMAGES_PER_ROUND); var correctImage = randomImages.ElementAt(indexOfCorrectImageInList); Round round = RoundFactory.Create(new List <Image>(), RoundsPlayedInGame(gameEntity.Id), ROUNDS_PER_GAME, correctImage.Name); roundEntity.CorrectImageId = correctImage.Id; _roundRepository.Create(roundEntity); foreach (ImageEntity imageEntity in randomImages) { ImageInRoundEntity imageInRoundEntity = ImageInRoundEntityFactory.Create(imageEntity.Id, roundEntity.Id); _imageInRoundRepository.Create(imageInRoundEntity); round.Images.Add(ImageFactory.Create(imageEntity.Url, imageEntity.Id)); } return(round); }
public RoundInfo GetLatestRoundInfo(int userId) {//TODO optimize GameEntity lastGame = _gameRepository.GetLatestGameForUser(userId); RoundEntity lastRound = _roundRepository.GetAll().Where(x => x.GameId == lastGame.Id).OrderByDescending(x => x.Id).FirstOrDefault(); return(GetRoundInfo(lastRound.Id)); }
private void DisplayRoundData(RoundEntity roundResult, int roundNumber) { boxResults.Text += $"Round {roundNumber}\r\n"; boxResults.Text += $"Round Type: {roundResult.RoundType}\r\n"; boxResults.Text += $"Qualified: {roundResult.Qualified}\r\n"; boxResults.Text += $"Kudos Earned: {roundResult.Kudos}\r\n"; boxResults.Text += $"Badge Earned: {roundResult.Badge}\r\n"; boxResults.Text += $"\r\n"; }
public static Round Create(RoundEntity roundEntity, int totalRounds, int amountOfRoundsPlayed) { Round round = new Round { Name = roundEntity.CorrectImage.Name, TotalRounds = totalRounds, AmountOfRoundsPlayed = amountOfRoundsPlayed, Images = roundEntity.ImagesInRound.Select(ImageFactory.Create).ToList() }; return(round); }
public virtual RoundEntity GetRoundWithLegs(long roundId) { var round = new RoundEntity(roundId); IPrefetchPath2 prefetchPathRoundLegs = new PrefetchPath2(EntityType.RoundEntity); prefetchPathRoundLegs.Add(RoundEntity.PrefetchPathRoundLegs); using var da = _dbContext.GetNewAdapter(); da.FetchEntity(round, prefetchPathRoundLegs); da.CloseConnection(); return(round); }
public virtual void SetRoundCompleted(RoundEntity round) { if (!new MatchRepository(_appDb.DbContext).AllMatchesCompleted(round)) { throw new ArgumentException($"Round {round.Id} has uncompleted matches."); } using var da = _appDb.DbContext.GetNewAdapter(); da.FetchEntity(round); round.IsComplete = true; round.ModifiedOn = DateTime.Now; da.SaveEntity(round); da.CloseConnection(); }
public bool AnswerRound(int answerImageId, int userId) { GameEntity currentGameForPlayer = _gameRepository.GetLatestGameForUser(userId); // id should be replaced with start date RoundEntity currentRound = _roundRepository.GetCurrentOpenRound(currentGameForPlayer.Id); ImageEntity answerImage = _imageRepository.Get(answerImageId); currentRound.GuessedImageId = answerImage.Id; currentGameForPlayer.Duration = DateTime.Now - currentGameForPlayer.StartDate; _gameRepository.Update(currentGameForPlayer); _roundRepository.Update(currentRound); return(currentRound.GuessedImageId == currentRound.CorrectImageId); }
public virtual EntityCollection <MatchEntity> GetMatches(RoundEntity round) { IRelationPredicateBucket bucket = new RelationPredicateBucket(); IPredicateExpression roundFilter = new PredicateExpression(new FieldCompareRangePredicate(MatchFields.RoundId, null, false, new[] { round.Id })); bucket.PredicateExpression.AddWithAnd(roundFilter); var matches = new EntityCollection <MatchEntity>(); using var da = _dbContext.GetNewAdapter(); da.FetchEntityCollection(matches, bucket); da.CloseConnection(); return(matches); }
public EnterResultViewModel(TournamentEntity tournament, RoundEntity round, MatchEntity match, MatchRuleEntity matchRule, IList <TeamInRoundEntity> teamInRound, Axuno.Tools.DateAndTime.TimeZoneConverter timeZoneConverter) { Tournament = tournament ?? throw new ArgumentNullException(nameof(tournament)); Round = round ?? throw new ArgumentNullException(nameof(round)); Match = match ?? throw new ArgumentNullException(nameof(match)); Opponent = new Opponent( teamInRound.FirstOrDefault(o => o.TeamId == match.HomeTeamId)?.TeamNameForRound ?? throw new ArgumentNullException(nameof(teamInRound)), teamInRound.FirstOrDefault(o => o.TeamId == match.GuestTeamId)?.TeamNameForRound ?? throw new ArgumentNullException(nameof(teamInRound)));; TimeZoneConverter = timeZoneConverter ?? throw new ArgumentNullException(nameof(timeZoneConverter)); _localizer = CreateModelStringLocalizer(); _maxNumberOfSets = matchRule.MaxNumOfSets(); MapEntityToFormFields(); }
public RoundInfo GetRoundInfo(int roundId) {//TODO optimize RoundInfo roundInfo = new RoundInfo(); RoundEntity roundEntity = _roundRepository.Get(roundId); IEnumerable <ImageInRoundEntity> imageInRoundEntity = _imageInRoundRepository.GetAll().Where(x => x.RoundId == roundId); roundInfo.GameId = roundEntity.GameId; roundInfo.CorrectImageId = roundEntity.CorrectImageId; roundInfo.GuessedImageId = roundEntity.GuessedImageId.Value; roundInfo.Name = _imageRepository.Get(roundEntity.CorrectImageId).Name; roundInfo.Images = imageInRoundEntity.Select(x => new Image { Id = x.ImageId, Url = _imageRepository.Get(x.ImageId).Url }).ToList(); roundInfo.AmountOfRoundsPlayed = RoundsPlayedInGame(roundEntity.GameId); roundInfo.TotalRounds = ROUNDS_PER_GAME; return(roundInfo); }
public static List <EpisodeEntity> GetEpisodesFromLog() { List <EpisodeEntity> allEpisodes = new List <EpisodeEntity>(); var playerLogData = ReadLogData(); var episodeStartingPoints = playerLogData.FindAll(data => data.Contains("[CompletedEpisodeDto]")); foreach (string episodeStartingPoint in episodeStartingPoints) { var episodeToAdd = new EpisodeEntity(); //get timestamp var episodeTimestamp = TimestampRegex.Match(episodeStartingPoint); string episodeData = ""; int startIndex = playerLogData.IndexOf(episodeStartingPoint); int endIndex = playerLogData.FindIndex(startIndex + 2, timestamp => Regex.IsMatch(timestamp, TimestampPattern)); for (int i = startIndex; i <= endIndex; i++) { episodeData += playerLogData[i]; } episodeToAdd = GetEpisodeStats(episodeData); // Get timestamps episodeToAdd.Timestamp = episodeTimestamp.Groups[1].Value; DateTime episodeFinished; if (DateTime.TryParse(episodeToAdd.Timestamp, out episodeFinished)) { episodeToAdd.EpisodeFinished = episodeFinished; } //get rounds List <RoundEntity> roundsInEpisode = new List <RoundEntity>(); for (int i = 0; i < episodeToAdd.RoundsPlayed; i++) { RoundEntity evaluatedRound = GetRoundStats(episodeData, i); roundsInEpisode.Add(evaluatedRound); } episodeToAdd.RoundEntities = roundsInEpisode; allEpisodes.Add(episodeToAdd); } return(allEpisodes); }
private static List <RoundEntity> GetRoundsFromString(string roundString) { var roundMatches = Regex.Matches(roundString, roundPattern, RegexOptions.Singleline); var results = new List <RoundEntity>(); foreach (Match roundMatch in roundMatches) { var round = new RoundEntity(); if (roundMatch.Success) { round.RoundType = roundMatch.Groups["roundName"].Value ?? string.Empty; round.Qualified = Util.BoolParse(roundMatch.Groups["qualified"].Value); round.Position = Util.IntParse(roundMatch.Groups["position"].Value); round.Kudos = Util.IntParse(roundMatch.Groups["kudos"].Value); round.Fame = Util.IntParse(roundMatch.Groups["fame"].Value); round.BonusTier = Util.IntParse(roundMatch.Groups["bonusTier"].Value); round.BonusKudos = Util.IntParse(roundMatch.Groups["bonusKudos"].Value); round.BonusFame = Util.IntParse(roundMatch.Groups["bonusFame"].Value); round.Badge = roundMatch.Groups["badge"].Value ?? string.Empty; results.Add(round); } } return(results); }
public async Task GenerateAvailableMatchDatesAsync(ClearMatchDates clearMatchDates, RoundEntity round, CancellationToken cancellationToken) { _ = await _availableMatchDates.ClearAsync(clearMatchDates, cancellationToken); if (!AreEntitiesLoaded) { await LoadEntitiesAsync(cancellationToken); } await _availableMatchDates.GenerateNewAsync(round, cancellationToken); }
/// <summary> /// Generate available match dates for teams where /// <see cref="TeamEntity.MatchDayOfWeek"/>, <see cref="TeamEntity.MatchTime"/>, <see cref="TeamEntity.VenueId"/> /// are not <see langword="null"/>. /// </summary> /// <param name="round"></param> /// <param name="cancellationToken"></param> /// <returns></returns> internal async Task GenerateNewAsync(RoundEntity round, CancellationToken cancellationToken) { await Initialize(cancellationToken); var teamIdProcessed = new List <long>(); var listTeamsWithSameVenue = new List <EntityCollection <TeamEntity> >(); // Make a list of teams of the same round and with the same venue AND weekday AND match time // Venues will later be assigned to these teams alternately foreach (var team in round.TeamCollectionViaTeamInRound) { // the collection will contain at least one team var teams = GetTeamsWithSameVenueAndMatchTime(team, round); if (teamIdProcessed.Contains(teams[0].Id)) { continue; } listTeamsWithSameVenue.Add(teams); foreach (var t in teams) { if (!teamIdProcessed.Contains(t.Id)) { teamIdProcessed.Add(t.Id); } } } foreach (var roundLeg in round.RoundLegs) { var startDate = DateTime.SpecifyKind(roundLeg.StartDateTime, DateTimeKind.Utc); var endDate = DateTime.SpecifyKind(roundLeg.EndDateTime, DateTimeKind.Utc); foreach (var teamsWithSameVenue in listTeamsWithSameVenue) { var teamIndex = 0; // Make sure these values are not null if (!teamsWithSameVenue[teamIndex].MatchDayOfWeek.HasValue || !teamsWithSameVenue[teamIndex].MatchTime.HasValue || !teamsWithSameVenue[teamIndex].VenueId.HasValue) { continue; } // Create Tuple for non-nullable context var team = (Id : teamsWithSameVenue[teamIndex].Id, MatchDayOfWeek : (DayOfWeek)teamsWithSameVenue[teamIndex].MatchDayOfWeek !.Value, MatchTime : teamsWithSameVenue[teamIndex].MatchTime !.Value, VenueId : teamsWithSameVenue[teamIndex].VenueId !.Value); // get the first possible match date equal or after the leg's starting date var matchDate = IncrementDateUntilDayOfWeek(startDate, team.MatchDayOfWeek); // process the period of a leg while (matchDate <= endDate) { // if there is more than one team per venue with same weekday and match time, // match dates will be assigned alternately var matchDateAndTimeUtc = _timeZoneConverter.ToUtc(matchDate.Date.Add(team.MatchTime)); // check whether the calculated date // is within the borders of round legs (if any) and is not marked as excluded if (IsDateWithinRoundLegDateTime(roundLeg, matchDateAndTimeUtc) && !IsExcludedDate(matchDateAndTimeUtc, round.Id, team.Id) && !await IsVenueOccupiedByMatchAsync( new DateTimePeriod(matchDateAndTimeUtc, matchDateAndTimeUtc.Add(_tenantContext.TournamentContext.FixtureRuleSet .PlannedDurationOfMatch)), team.VenueId, cancellationToken)) { var av = new AvailableMatchDateEntity { TournamentId = _tenantContext.TournamentContext.MatchPlanTournamentId, HomeTeamId = team.Id, VenueId = team.VenueId, MatchStartTime = matchDateAndTimeUtc, MatchEndTime = matchDateAndTimeUtc.Add(_tenantContext.TournamentContext.FixtureRuleSet.PlannedDurationOfMatch), IsGenerated = true }; _generatedAvailableMatchDateEntities.Add(av); teamIndex = ++teamIndex >= teamsWithSameVenue.Count ? 0 : teamIndex; } matchDate = matchDate.Date.AddDays(7); } } } _logger.LogTrace("Generated {Count} UTC dates for HomeTeams:", _generatedAvailableMatchDateEntities.Count); _logger.LogTrace("{Generated}\n", _generatedAvailableMatchDateEntities.Select(gen => (gen.HomeTeamId, gen.MatchStartTime))); // save to the persistent storage // await _appDb.GenericRepository.SaveEntitiesAsync(_generatedAvailableMatchDateEntities, true, false, cancellationToken); }
private EntityCollection <TeamEntity> GetTeamsWithSameVenueAndMatchTime(TeamEntity team, RoundEntity round) { var resultTeams = new EntityCollection <TeamEntity>(); var teamStartTime = team.MatchTime; var teamEndTime = teamStartTime?.Add(_tenantContext.TournamentContext.FixtureRuleSet.PlannedDurationOfMatch); // get a list of other teams in this round with same venue and match day-of-the-week var otherTeams = round.TeamCollectionViaTeamInRound.FindMatches((TeamFields.VenueId == team.VenueId) & (TeamFields.MatchDayOfWeek == team.MatchDayOfWeek) & (TeamFields.Id != team.Id)); foreach (var index in otherTeams) { var otherStartTime = round.TeamCollectionViaTeamInRound[index].MatchTime; var otherEndTime = otherStartTime?.Add(_tenantContext.TournamentContext.FixtureRuleSet.PlannedDurationOfMatch); if (otherStartTime <= teamStartTime && otherEndTime >= teamStartTime || otherStartTime <= teamEndTime && otherEndTime >= teamEndTime) { resultTeams.Add(round.TeamCollectionViaTeamInRound[index]); } } // list is expected to contain at least one team resultTeams.Add(team); return(resultTeams); }
private EntityCollection <TeamEntity> GetTeamsWithSameVenueAndMatchTime(TeamEntity team, RoundEntity round) { var resultTeams = new EntityCollection <TeamEntity>(); TimeSpan?teamStartTime = team.MatchTime; TimeSpan?teamEndTime = teamStartTime?.Add(_matchPlanner.PlannedDurationOfMatch); // first get a list of other teams with same venue and match day of the week List <int> tmpTeams = round.TeamCollectionViaTeamInRound.FindMatches(TeamFields.VenueId == team.VenueId & TeamFields.MatchDayOfWeek == (int)team.MatchDayOfWeek & TeamFields.Id != team.Id); foreach (var index in tmpTeams) { TimeSpan?otherStartTime = round.TeamCollectionViaTeamInRound[index].MatchTime; TimeSpan?otherEndTime = otherStartTime?.Add(_matchPlanner.PlannedDurationOfMatch); if ((otherStartTime <= teamStartTime && otherEndTime >= teamStartTime) || (otherStartTime <= teamEndTime && otherEndTime >= teamEndTime)) { resultTeams.Add(round.TeamCollectionViaTeamInRound[index]); } } // list is expected to contain at least one team resultTeams.Add(team); return(resultTeams); }
internal void GenerateNew(RoundEntity round) { var teamIdProcessed = new List <long>(); var listTeamsWithSameVenue = new List <EntityCollection <TeamEntity> >(); // Make a list of teams of the same round and with the same venue + weekday + match time // Venues will later be assigned to these teams in an alternating way foreach (var team in round.TeamCollectionViaTeamInRound) { // the collection will contain at least one team EntityCollection <TeamEntity> teams = GetTeamsWithSameVenueAndMatchTime(team, round); if (!teamIdProcessed.Contains(teams[0].Id)) { listTeamsWithSameVenue.Add(teams); foreach (var t in teams) { if (!teamIdProcessed.Contains(t.Id)) { teamIdProcessed.Add(t.Id); } } } } foreach (var roundLeg in round.RoundLegs) { DateTime startDate = roundLeg.StartDateTime; DateTime endDate = roundLeg.EndDateTime; var matchDate = new DateTime(); foreach (var teamsWithSameVenue in listTeamsWithSameVenue) { int teamIndex = 0; // get the first possible match date equal or after the leg's starting date matchDate = IncrementDateUntilDayOfWeek(startDate, (DayOfWeek)teamsWithSameVenue[teamIndex].MatchDayOfWeek); // process the period of a leg while (matchDate <= endDate) { // if there is more than one team per venue with same weekday and match time, // match dates will be assigned alternately TeamEntity team = teamsWithSameVenue[teamIndex]; DateTime matchDateAndTime = DateTime.Parse(string.Concat(matchDate.ToShortDateString(), " ", team.MatchTime)); // check whether the calculated date // is within the borders of round legs (if any) and is not marked as excluded if (IsDateWithinRoundLegDateTime(roundLeg, matchDate) && !IsExcludedDate(matchDate, round.Id, team.Id) && !IsVenueOccupiedByMatch(new DateTimePeriod(matchDateAndTime, matchDateAndTime.Add(_matchPlanner.PlannedDurationOfMatch)), team.VenueId.Value)) { var av = new AvailableMatchDateEntity(); av.TournamentId = _matchPlanner.Tournament.Id; av.HomeTeamId = team.Id; av.VenueId = team.Venue.Id; av.MatchStartTime = matchDateAndTime; av.MatchEndTime = matchDateAndTime.Add(_matchPlanner.PlannedDurationOfMatch); av.IsGenerated = true; av.CreatedOn = DateTime.Now; _availableMatchDate.Add(av); teamIndex = (++teamIndex >= teamsWithSameVenue.Count) ? 0 : teamIndex; } matchDate = matchDate.Date.AddDays(7); } } } // save to the persistent storage _matchPlanner.Adapter.SaveEntityCollection(_availableMatchDate, true, false); }
public static RoundEntity GetRoundStats(string roundData, int roundNumber) { RoundEntity roundResults = new RoundEntity(); // Get round type int roundIndex = roundData.IndexOf($"[Round {roundNumber}"); string roundCodeStateText = roundData.Substring(roundIndex, 50); Regex roundCodeRegex = new Regex(@"\[Round \d \| (.+)\]"); Match roundCodeState = roundCodeRegex.Match(roundCodeStateText); // Get qualification status for round int qualifiedIndex = roundData.IndexOf("Qualified:", roundIndex); string qualifiedStateText = roundData.Substring(qualifiedIndex, 25); Regex qualifiedRegex = new Regex(@"Qualified:.([a-zA-Z]+)"); Match qualifiedState = qualifiedRegex.Match(qualifiedStateText); // Get starting position in round int positionIndex = roundData.IndexOf("Position:", roundIndex); string positionStateText = roundData.Substring(positionIndex, 25); Regex positionRegex = new Regex(@"Position:.(\d+)"); Match positionState = positionRegex.Match(positionStateText); // Get Kudos earned in round int kudosIndex = roundData.IndexOf("Kudos:", roundIndex); string kudosStateText = roundData.Substring(kudosIndex, 25); Regex kudosRegex = new Regex(@"Kudos:.(\d+)"); Match kudosState = kudosRegex.Match(kudosStateText); // Get Fame earned in round int fameIndex = roundData.IndexOf("Fame:", roundIndex); string fameStateText = roundData.Substring(fameIndex, 25); Regex fameRegex = new Regex(@"Fame:.(\d+)"); Match fameState = fameRegex.Match(fameStateText); // Get round's bonus tier int bonusTierIndex = roundData.IndexOf("Bonus Tier:", roundIndex); string bonusTierStateText = roundData.Substring(bonusTierIndex, 25); Regex bonusTierRegex = new Regex(@"Bonus Tier:.(\d+)"); Match bonusTierState = bonusTierRegex.Match(bonusTierStateText); int bonusTier; if (bonusTierState.Captures.Count == 0) { bonusTier = -1; } else { bonusTier = Int32.Parse(bonusTierState.Groups[1].Value); } // Get round's bonus Kudos int bonusKudosIndex = roundData.IndexOf("Bonus Kudos:", roundIndex); string bonusKudosStateText = roundData.Substring(bonusKudosIndex, 25); Regex bonusKudosRegex = new Regex(@"Bonus Kudos:.(\d+)"); Match bonusKudosState = bonusKudosRegex.Match(bonusKudosStateText); // Get round's bonus Fame int bonusFameIndex = roundData.IndexOf("Bonus Fame:", roundIndex); string bonusFameStateText = roundData.Substring(bonusFameIndex, 16); Regex bonusFameRegex = new Regex(@"Bonus Fame:.(\d+)"); Match bonusFameState = bonusFameRegex.Match(bonusFameStateText); // Get round's earned badge int badgeIndex = roundData.IndexOf("BadgeId:", roundIndex); string badgeStateText = roundData.Substring(badgeIndex, 16); Regex badgeRegex = new Regex(@"BadgeId:.([a-zA-Z]+)"); Match badgeState = badgeRegex.Match(badgeStateText); string badgeId = "failure"; if (bonusTierState.Captures.Count == 0 && qualifiedState.Groups[1].Value == "True") { badgeId = "Passed"; } else if (qualifiedState.Groups[1].Value == "False") { badgeId = "Failure"; } else { badgeId = badgeState.Groups[1].Value; } // Assemble round data object roundResults.RoundType = roundCodeState.Groups[1].Value; roundResults.Qualified = (qualifiedState.Groups[1].Value == "True"); roundResults.Position = Int32.Parse(positionState.Groups[1].Value); roundResults.Kudos = Int32.Parse(kudosState.Groups[1].Value); roundResults.Fame = Int32.Parse(fameState.Groups[1].Value); roundResults.BonusTier = bonusTier; roundResults.BonusKudos = Int32.Parse(bonusKudosState.Groups[1].Value); roundResults.BonusFame = Int32.Parse(bonusFameState.Groups[1].Value); roundResults.Badge = badgeId; return(roundResults); }
/// <summary> /// Copies the round basic data and the round leg data /// from the source to an existing target tournament. The new tournament id must /// already exist. Leg data for each round is taken over from target tournament legs /// on a 1:1 base (same number of legs, dates/times). /// </summary> /// <param name="fromTournamentId">Existing source tournament id.</param> /// <param name="toTournamentId">Existing target tournament id.</param> /// <param name="excludeRoundId">List of round id's to be excluded (may be null for none)</param> /// <returns>True, if creation was successful, false otherwise.</returns> public bool CopyRound(long fromTournamentId, long toTournamentId, IEnumerable <long> excludeRoundId) { const string transactionName = "CloneRounds"; if (excludeRoundId == null) { excludeRoundId = new List <long>(); } DateTime now = DateTime.Now; // get the rounds of SOURCE tournament var roundIds = _appDb.TournamentRepository.GetTournamentRounds(fromTournamentId).Select(r => r.Id).ToList(); using var da = _appDb.DbContext.GetNewAdapter(); // da.StartTransaction(System.Data.IsolationLevel.ReadUncommitted, transactionName); var roundsWithLegs = new Queue <RoundEntity>(); foreach (var r in roundIds) { roundsWithLegs.Enqueue(_appDb.RoundRepository.GetRoundWithLegs(r)); } foreach (var r in roundIds) { var round = roundsWithLegs.Dequeue(); // skip excluded round id's if (excludeRoundId.Contains(r)) { continue; } // create new round and overtake data of source round var newRound = new RoundEntity() { TournamentId = toTournamentId, Name = round.Name, Description = round.Description, TypeId = round.TypeId, NumOfLegs = round.NumOfLegs, MatchRuleId = round.MatchRuleId, SetRuleId = round.MatchRuleId, IsComplete = false, CreatedOn = now, ModifiedOn = now, NextRoundId = null }; // create the round leg records based on the TARGET tournament legs foreach (var rl in round.RoundLegs) { var newRoundLeg = new RoundLegEntity() { SequenceNo = rl.SequenceNo, Description = rl.Description, StartDateTime = rl.StartDateTime, EndDateTime = rl.EndDateTime, CreatedOn = now, ModifiedOn = now }; newRound.RoundLegs.Add(newRoundLeg); } // save recursively (new round with its new round legs) if (!da.SaveEntity(newRound, true, true)) { // roll back if any round fails da.Rollback(transactionName); return(false); } } // commit only after all rounds are processed successfully da.Commit(); return(true); }
/// <summary> /// Generates round match combinations for the Round Robin system, /// assigns optimized match dates and stores the matches to /// the persistent storage. /// </summary> public async Task GenerateFixturesForRound(RoundEntity round, bool keepExisting, CancellationToken cancellationToken) { if (!AreEntitiesLoaded) { await LoadEntitiesAsync(cancellationToken); } round = _tournament.Rounds.First(r => r.Id == round.Id); if (_appDb.MatchRepository.AnyCompleteMatchesExist(round)) { throw new Exception($"Completed matches exist for round '{round.Id}'. Generating fixtures aborted."); } // generated matches will be stored here var roundMatches = new EntityCollection <MatchEntity>(); if (keepExisting) { roundMatches = _appDb.MatchRepository.GetMatches(round); } else { var bucket = new RelationPredicateBucket(new PredicateExpression( new FieldCompareRangePredicate(MatchFields.RoundId, null, false, new[] { round.Id }))); await _appDb.GenericRepository.DeleteEntitiesDirectlyAsync(typeof(MatchEntity), bucket, cancellationToken); } await GenerateAvailableMatchDatesAsync(ClearMatchDates.OnlyAutoGenerated, round, cancellationToken); // get the team ids because TeamEntity lacks IComparable // and cannot be used directly var teams = new Collection <long>(round.TeamCollectionViaTeamInRound.Select(t => t.Id).ToList()); // now calculate matches for each leg of a round foreach (var roundLeg in round.RoundLegs) { // build up match combinations for the teams of round var roundRobin = new RoundRobinSystem <long>(teams); var bundledGroups = roundRobin.GetBundledGroups(RefereeType.HomeTeam, roundLeg.SequenceNo % 2 == 1 ? LegType.First : LegType.Return, CombinationGroupOptimization.GroupWithAlternatingHomeGuest); /* * Special treatment for teams which do not have home matches */ foreach (var teamCombinationGroup in bundledGroups) { foreach (var combination in teamCombinationGroup) { if (!TeamsWithoutHomeMatches.Contains(combination.HomeTeam)) { continue; } // swap home and guest team, keep referee unchanged (combination.HomeTeam, combination.GuestTeam) = (combination.GuestTeam, combination.HomeTeam); } } /* * Assign desired from/to dates to bundled groups for later orientation * in which period matches should take place */ AssignRoundDatePeriods(roundLeg, bundledGroups); if (bundledGroups.Any(g => !g.DateTimePeriod.Start.HasValue)) { throw new Exception( "Not all bundled groups got a date period assigned. Probably not enough dates available for assignment."); } // process each team combination (match) that shall take place in the same week (if possible) foreach (var teamCombinationGroup in bundledGroups) { // get match dates for every combination of a group. // matches in the same teamCombinationGroup can even take place on the same day. // matchDates contains calculated dates in the same order as combinations, // so the index can be used for both. var availableDates = GetMatchDates(roundLeg, teamCombinationGroup, roundMatches); _logger.LogTrace("Selected dates: {0}", string.Join(", ", availableDates.OrderBy(bd => bd?.MatchStartTime).Select(bd => bd?.MatchStartTime.ToShortDateString())).TrimEnd(',', ' ')); for (var index = 0; index < teamCombinationGroup.Count; index++) { var combination = teamCombinationGroup[index]; // If existing matches were loaded from database, we have to skip such combinations! // Note: Home team and guest team of combinations could have been swapped for TeamsWithoutHomeMatches if (roundMatches.Any(rm => rm.HomeTeamId == combination.HomeTeam && rm.GuestTeamId == combination.GuestTeam && rm.LegSequenceNo == roundLeg.SequenceNo || rm.GuestTeamId == combination.HomeTeam && rm.HomeTeamId == combination.GuestTeam && rm.LegSequenceNo == roundLeg.SequenceNo)) { continue; } var match = new MatchEntity { HomeTeamId = combination.HomeTeam, GuestTeamId = combination.GuestTeam, RefereeId = combination.Referee, PlannedStart = availableDates[index] != null ? availableDates[index] !.MatchStartTime : default(DateTime?), PlannedEnd = availableDates[index] != null ? availableDates[index] !.MatchStartTime .Add(_tenantContext.TournamentContext.FixtureRuleSet.PlannedDurationOfMatch) : default(DateTime?), VenueId = availableDates[index] != null ? availableDates[index] !.VenueId // take over the venue stored in the team entity (may also be null!) : _tournament.Rounds[_tournament.Rounds.FindMatches(RoundFields.Id == roundLeg.RoundId).First()].TeamCollectionViaTeamInRound.First(t => t.Id == combination.HomeTeam).VenueId, RoundId = round.Id, IsComplete = false, LegSequenceNo = roundLeg.SequenceNo, ChangeSerial = 0, Remarks = string.Empty }; roundMatches.Add(match); } } } // save the matches for the group await _appDb.GenericRepository.SaveEntitiesAsync(roundMatches, true, false, cancellationToken); await _availableMatchDates.ClearAsync(ClearMatchDates.OnlyAutoGenerated, cancellationToken); }