public void GroupOddDrawCompetitors() { var settings = new DistanceDrawSettings { GroupMode = DistanceDrawGroupMode.Time, GroupSize = 2 }; var sortedCompetitors = expert.SortDrawCompetitors(distance, drawCompetitors, settings); var groups = expert.GroupDrawCompetitors(distance, 1, sortedCompetitors, settings); Assert.AreEqual(3, groups.Count); Assert.AreEqual(2, groups[0].Count); Assert.AreEqual(2, groups[1].Count); Assert.AreEqual(1, groups[2].Count); Assert.IsTrue(groups.SelectMany(g => g).SequenceEqual(new[] { drawCompetitors[1], drawCompetitors[4], drawCompetitors[0], drawCompetitors[3], drawCompetitors[2] })); }
public async Task <DistanceDrawSettings> GetSettingsOrDefaultAsync(Guid competitionId) { var settings = await Settings.FirstOrDefaultAsync(s => s.CompetitionId == competitionId); if (settings == null) { settings = new DistanceDrawSettings { CompetitionId = competitionId }; context.DistanceDrawSettings.Add(settings); } return(settings); }
public override Task DrawCompetitorGroupAsync(Distance distance, IReadOnlyCollection <Guid> distanceCombinations, int round, IList <CompetitorBase> competitors, ICompetitionContext context, DistanceDrawSettings settings) { switch (settings.Mode) { case DistanceDrawMode.Random: case DistanceDrawMode.SwitchLanesOrRandom: competitors.Shuffle(); return(Task.FromResult <object>(null)); default: throw new DrawModeNotSupportedException(); } }
public void DrawOddQuartetsRandomReverse() { var settings = new DistanceDrawSettings { GroupMode = DistanceDrawGroupMode.Time, GroupSize = 2, ReverseFilling = true }; var sortedCompetitors = expert.SortDrawCompetitors(distance, drawCompetitors, settings); var groups = expert.GroupDrawCompetitors(distance, 1, sortedCompetitors, settings); var competitorGroups = new List <IReadOnlyList <CompetitorBase> >(); foreach (var group in groups) { var competitorGroup = group.Select(g => g.Competitor).ToList(); expert.DrawCompetitorGroupAsync(distance, new[] { distanceCombination.Id }, 1, competitorGroup, context, settings).Wait(); competitorGroups.Add(competitorGroup); } var heats = expert.FillCompetitorsInHeatsAsync(distance, new[] { distanceCombination.Id }, 1, competitorGroups, context, settings).Result; var races = heats.SelectMany(r => r.Value).ToList(); Assert.IsTrue(Enumerable.Range(distance.FirstHeat, 4).SequenceEqual(heats.Keys)); Assert.AreEqual(2, heats[9].Count); Assert.AreEqual(2, heats[10].Count); Assert.AreEqual(0, heats[11].Count); Assert.AreEqual(1, heats[12].Count); Assert.AreEqual(5, races.Count); Assert.AreEqual(0, races[0].Lane); Assert.AreEqual(1, races[1].Lane); Assert.AreEqual(0, races[2].Lane); Assert.AreEqual(1, races[3].Lane); Assert.AreEqual(0, races[4].Lane); }
public async Task <IReadOnlyList <IReadOnlyList <DrawCompetitor> > > GroupAsync(Distance distance, IEnumerable <Guid> distanceCombinations, int round, DistanceDrawSettings settings, params IHistoricalTimeSelector[] selectors) { if (distance == null) { throw new ArgumentNullException(nameof(distance)); } if (distanceCombinations == null) { throw new ArgumentNullException(nameof(distanceCombinations)); } if (round < 1) { throw new ArgumentOutOfRangeException(nameof(round)); } Debug.Assert(distance.Competition != null); var distanceExpert = distanceExpertManager.Find(distance.Discipline); if (distanceExpert == null) { throw new InvalidDisciplineException(); } var confirmedCompetitors = ConfirmedCompetitors(distance, distanceCombinations, round); var roundCompetitors = await distanceExpert.SelectCompetitorsForRound(distance, round, confirmedCompetitors).ToListAsync(); var drawCompetitors = new List <DrawCompetitor>(); switch (settings.GroupMode) { case DistanceDrawGroupMode.Category: drawCompetitors.AddRange(roundCompetitors.Select(rc => new DrawCompetitor(rc, null))); break; case DistanceDrawGroupMode.Time: foreach (var competitor in roundCompetitors) { var personCompetitor = competitor as PersonCompetitor; IPersonLicenseTime time = null; if (personCompetitor != null) { time = await personTimesWorkflow.FindHistoricalTimeAsync(distance.Competition.LicenseIssuerId, distance.Competition.Discipline, distance.Discipline, distance.Value, competitor.LicenseKey, selectors); } drawCompetitors.Add(new DrawCompetitor(competitor, time)); } break; } var categories = await context.PersonCategories .Where(c => c.LicenseIssuerId == distance.Competition.LicenseIssuerId && c.Discipline == distance.Competition.Discipline).ToListAsync(); var sortedDrawCompetitors = distanceExpert.SortDrawCompetitors(distance, drawCompetitors, settings, categories).ToList(); return(distanceExpert.GroupDrawCompetitors(distance, round, sortedDrawCompetitors, settings)); }
public async Task UpdateSettingsAsync(DistanceDrawSettings settings) { await context.SaveChangesAsync(); }
public async Task <IReadOnlyDictionary <int, IReadOnlyCollection <Race> > > DrawAsync(Distance distance, IReadOnlyCollection <Guid> distanceCombinations, int round, IReadOnlyList <IReadOnlyList <Guid> > groups, DistanceDrawSettings settings) { if (distance == null) { throw new ArgumentNullException(nameof(distance)); } if (round < 1) { throw new ArgumentOutOfRangeException(nameof(round)); } Debug.Assert(distance.Competition != null); var distanceExpert = distanceExpertManager.Find(distance.Discipline); if (distanceExpert == null) { throw new InvalidDisciplineException(); } using (var transaction = context.BeginTransaction(IsolationLevel.RepeatableRead)) try { if (settings.DeleteExisting) { await DeleteRacesAsync(distance, round); } var confirmedCompetitors = ConfirmedCompetitors(distance, distanceCombinations, round); var roundCompetitors = await distanceExpert.SelectCompetitorsForRound(distance, round, confirmedCompetitors).ToDictionaryAsync(c => c.Id); var competitorGroups = new List <IReadOnlyList <CompetitorBase> >(); foreach (var group in groups) { var competitors = new List <CompetitorBase>(); foreach (var competitorId in group) { CompetitorBase competitor; if (!roundCompetitors.TryGetValue(competitorId, out competitor)) { throw new CompetitorNotFoundException(); } competitors.Add(competitor); } await distanceExpert.DrawCompetitorGroupAsync(distance, distanceCombinations, round, competitors, context, settings); competitorGroups.Add(competitors.AsReadOnly()); } await distancesWorkflow.ResetFirstHeatAsync(distance, distance.ContinuousNumbering, !distance.ContinuousNumbering ?distance.FirstHeat : new int?()); var races = await distanceExpert.FillCompetitorsInHeatsAsync(distance, distanceCombinations, round, competitorGroups, context, settings); foreach (var heat in races.Keys) { await distancesWorkflow.AddRacesToHeatAsync(distance, round, heat, true, races[heat].ToArray()); } await context.SaveChangesAsync(); transaction.Commit(); return(races); } catch { transaction.Rollback(); throw; } }
public abstract Task <IReadOnlyDictionary <int, IReadOnlyCollection <Race> > > FillCompetitorsInHeatsAsync(Distance distance, IReadOnlyCollection <Guid> distanceCombinations, int round, IReadOnlyList <IReadOnlyList <CompetitorBase> > competitorGroups, ICompetitionContext context, DistanceDrawSettings settings);
public virtual Task DrawCompetitorGroupAsync(Distance distance, IReadOnlyCollection <Guid> distanceCombinations, int round, IList <CompetitorBase> competitors, ICompetitionContext context, DistanceDrawSettings settings) { throw new DrawModeNotSupportedException(); }
public virtual IReadOnlyList <IReadOnlyList <DrawCompetitor> > GroupDrawCompetitors(Distance distance, int round, IReadOnlyList <DrawCompetitor> competitors, DistanceDrawSettings settings) { var groups = new List <IReadOnlyList <DrawCompetitor> >(); switch (settings.GroupMode) { case DistanceDrawGroupMode.Category: groups.AddRange(competitors.GroupBy(c => c.Competitor.Category.Substring(0, settings.CategoryLength)).Select(g => g.ToList())); break; case DistanceDrawGroupMode.Time: var count = 0; while (count < competitors.Count) { var group = competitors.Skip(count).Take(settings.GroupSize).ToList().AsReadOnly(); groups.Add(@group); count += @group.Count; } break; default: throw new ArgumentOutOfRangeException(); } return(groups.AsReadOnly()); }
public virtual IReadOnlyList <DrawCompetitor> SortDrawCompetitors(Distance distance, IEnumerable <DrawCompetitor> competitors, DistanceDrawSettings settings, IReadOnlyList <PersonCategory> categories) { return(competitors.ToList().AsReadOnly()); }
public override async Task <IReadOnlyDictionary <int, IReadOnlyCollection <Race> > > FillCompetitorsInHeatsAsync(Distance distance, IReadOnlyCollection <Guid> distanceCombinations, int round, IReadOnlyList <IReadOnlyList <CompetitorBase> > competitorGroups, ICompetitionContext context, DistanceDrawSettings settings) { var competitors = competitorGroups.SelectMany(g => g).ToList(); if (!settings.ReverseGroups) { competitors.Reverse(); } var pairCount = (competitors.Count + 1) / 2; var hasFillPair = distance.StartMode == DistanceStartMode.MultipleHeats && (pairCount % 2) == 1; if (hasFillPair) { pairCount++; } var firstHeat = distance.Races.Max(r => new int?(r.Heat)) + 1 ?? distance.FirstHeat; switch (settings.Mode) { case DistanceDrawMode.Random: return(FillHeatsByCompetitorsOrder(firstHeat, pairCount, hasFillPair, competitors, settings.ReverseFilling, settings.Spreading)); case DistanceDrawMode.SwitchLanesOrRandom: if (distanceCombinations.Count != 1) { throw new DrawModeNotSupportedException(); } var distanceCombinationId = distanceCombinations.Single(); var previousDistance = await(from d in context.Distances where d.CompetitionId == distance.CompetitionId && d.Number < distance.Number && d.Combinations.Any(dc => dc.Id == distanceCombinationId) orderby d.Number descending select new { d.Id, FirstPair = d.FirstHeat }).FirstOrDefaultAsync(); if (previousDistance == null) { return(FillHeatsByCompetitorsOrder(firstHeat, pairCount, hasFillPair, competitors, settings.ReverseFilling, settings.Spreading)); } var competitorLookup = competitors.ToDictionary(c => c.Id); var previousPairs = await(from r in context.Races where r.DistanceId == previousDistance.Id group r by r.Heat into heat select new { Pair = heat.Key, Races = from r in heat select new { r.Lane, r.CompetitorId } }).ToListAsync(); var seed = previousPairs.ToDictionary(p => p.Pair - previousDistance.FirstPair + firstHeat, p => p.Races.Where(r => competitorLookup.ContainsKey(r.CompetitorId)).ToDictionary(r => (r.Lane + 1) % 2, r => competitorLookup[r.CompetitorId])); return(FillHeatsByFixedLanes(firstHeat, pairCount, seed)); default: throw new DrawModeNotSupportedException(); } }
public override IReadOnlyList <DrawCompetitor> SortDrawCompetitors(Distance distance, IEnumerable <DrawCompetitor> competitors, DistanceDrawSettings settings, IReadOnlyList <PersonCategory> categories) { switch (settings.GroupMode) { case DistanceDrawGroupMode.Category: return(competitors.OrderByDescending(c => categories.FirstOrDefault(ca => ca.Code == c.Competitor.Category)?.FromAge ?? int.MaxValue) .ThenByDescending(c => c.Competitor.Category.Substring(0, 1)) .ToList() .AsReadOnly()); case DistanceDrawGroupMode.Time: return(competitors.OrderBy(c => c.Time?.Time ?? TimeSpan.MaxValue).ToList().AsReadOnly()); default: throw new ArgumentOutOfRangeException(); } }
public override Task <IReadOnlyDictionary <int, IReadOnlyCollection <Race> > > FillCompetitorsInHeatsAsync(Distance distance, IReadOnlyCollection <Guid> distanceCombinations, int round, IReadOnlyList <IReadOnlyList <CompetitorBase> > competitorGroups, ICompetitionContext context, DistanceDrawSettings settings) { throw new DrawModeNotSupportedException(); }