/// <summary> /// Checks if a person has more than two parents. /// </summary> private void CheckParents(RelationContext context) { foreach (var page in context.Pages.Values) { if (!context.Relations.TryGetValue(page.Id, out var rels)) { continue; } var parents = rels.Where(x => x.Type == RelationType.Parent) .ToList(); if (parents.Count > 2) { Error("У человека не может быть более двух биологических родителей", page.Id); } if (parents.Count == 2) { var p1 = context.Pages[parents[0].DestinationId]; var p2 = context.Pages[parents[1].DestinationId]; if (p1.Gender == p2.Gender && p1.Gender != null) { Error("Биологические родители не могут быть одного пола", p1.Id, p2.Id, page.Id); } } } }
/// <summary> /// Returns the list of all inferred relation groups for the page. /// </summary> public async Task <IReadOnlyList <RelationCategoryVM> > GetRelationsForPage(Guid pageId) { var ctx = await RelationContext.LoadContextAsync(_db); var cats = new [] { new RelationCategoryVM { Title = "Родственники", IsMain = true, Groups = GetGroups(ctx, pageId, ParentRelations) .Concat(GetSpouseGroups(ctx, pageId)) .Concat(GetGroups(ctx, pageId, OtherRelativeRelations)) .ToList() }, new RelationCategoryVM { Title = "Люди", Groups = GetGroups(ctx, pageId, NonRelativeRelations).ToList(), }, new RelationCategoryVM { Title = "Страницы", Groups = GetGroups(ctx, pageId, NonHumanRelations).ToList(), } }; return(cats.Where(x => x.Groups.Any()).ToList()); }
/// <summary> /// Checks the context for inconsistent wedding information. /// </summary> private void CheckWeddings(RelationContext context) { foreach (var rel in context.Relations.Values.SelectMany(x => x)) { if (rel.Type != RelationType.Spouse || rel.IsComplementary) { continue; } var first = context.Pages[rel.SourceId]; var second = context.Pages[rel.DestinationId]; if (first.BirthDate >= second.DeathDate || second.BirthDate >= first.DeathDate) { Error("Дата рождения одгого супруга не может быть раньше даты смерти другого", first.Id, second.Id); } if (rel.Duration is FuzzyRange dur) { if (dur.RangeStart < first.BirthDate || dur.RangeEnd > first.DeathDate) { Error("Брак должен быть ограничен временем жизни супруга", first.Id, second.Id); } if (dur.RangeStart < second.BirthDate || dur.RangeEnd > second.DeathDate) { Error("Брак должен быть ограничен временем жизни супруга", first.Id, second.Id); } } } }
/// <summary> /// Main loop. /// </summary> protected override async Task <bool> ProcessAsync(IServiceProvider services) { try { await services.GetRequiredService <StartupService>().WaitForStartup(); using (var db = services.GetService <AppDbContext>()) using (var js = services.GetService <INodeServices>()) { if (_flush) { await FlushTreeAsync(db); } var hasPages = await db.Pages.AnyAsync(x => x.TreeLayoutId == null); if (!hasPages) { return(true); } var opts = new RelationContextOptions { PeopleOnly = true }; var ctx = await RelationContext.LoadContextAsync(db, opts); var trees = GetAllSubtrees(ctx); _logger.Information($"Tree layout started: {ctx.Pages.Count} people, {ctx.Relations.Count} rels, {trees.Count} subtrees."); foreach (var tree in trees) { var rendered = await RenderTree(js, tree); var layout = new TreeLayout { Id = Guid.NewGuid(), LayoutJson = rendered, GenerationDate = DateTimeOffset.Now }; await SaveLayoutAsync(db, tree, layout); } _logger.Information("Tree layout completed."); } } catch (Exception ex) { if (!(ex is TaskCanceledException)) { _logger.Error(ex, "Failed to generate a tree layout."); } } return(true); }
/// <summary> /// Checks the lifespans consistency of each person/pet page. /// </summary> private void CheckLifespans(RelationContext context) { foreach (var page in context.Pages.Values) { if (page.BirthDate > page.DeathDate) { Error("Дата рождения не может быть позже даты смерти", page.Id); } } }
/// <summary> /// Checks the current relations and prepares them for database serialization. /// </summary> public async Task ValidateAsync(Page page, string rawFacts) { var context = await RelationContext.LoadContextAsync(_db); AugmentRelationContext(context, page, rawFacts); var core = new ValidatorCore(); core.Validate(context, new [] { page.Id }); core.ThrowIfInvalid(context, nameof(PageEditorVM.Facts)); }
/// <summary> /// Infers page-related events for the current month. /// </summary> private IEnumerable <CalendarEventVM> GetPageEvents(int year, int month, RelationContext context) { var maxDate = new FuzzyDate(new DateTime(year, month, 1).AddMonths(1).AddSeconds(-1)); foreach (var page in context.Pages.Values) { if (page.BirthDate is FuzzyDate birth) { var showBirth = birth.Month == month && (birth.Year == null || birth.Year <= year) && (page.DeathDate == null || page.DeathDate >= maxDate); if (showBirth) { var title = (year == birth.Year && !birth.IsDecade) ? "Дата рождения" : (birth.Year == null || birth.IsDecade) ? "День рождения" : $"День рождения ({year - birth.Year.Value})"; yield return(new CalendarEventVM { Day = birth.Day, Title = title, Type = CalendarEventType.Birth, RelatedPage = Map(page) }); } } if (page.DeathDate is FuzzyDate death) { var showDeath = death.Month == month && (death.Year == null || death.Year <= year); if (showDeath) { var title = (year == death.Year && !death.IsDecade) ? "Дата смерти" : (death.Year == null || death.IsDecade) ? "Годовщина смерти" : (year - death.Year.Value) + "-ая годовщина смерти"; yield return(new CalendarEventVM { Day = death.Day, Title = title, Type = CalendarEventType.Death, RelatedPage = Map(page) }); } } } }
/// <summary> /// Infers relation-based events for the current month. /// </summary> private IEnumerable <CalendarEventVM> GetRelationEvents(int year, int month, RelationContext context) { var visited = new HashSet <string>(); var maxDate = new FuzzyDate(CreateDate(year, month).AddMonths(1).AddSeconds(-1)); foreach (var rel in context.Relations.SelectMany(x => x.Value)) { if (!(rel.Duration is FuzzyRange duration) || !(duration.RangeStart is FuzzyDate start) || start.Month != month) { continue; } if (duration.RangeEnd is FuzzyDate end && end <= maxDate) { continue; } var hash = string.Concat(rel.SourceId.ToString(), rel.DestinationId.ToString(), duration.ToString()); if (visited.Contains(hash)) { continue; } var inverseHash = string.Concat(rel.DestinationId.ToString(), rel.SourceId.ToString(), duration.ToString()); visited.Add(hash); visited.Add(inverseHash); var title = (year == start.Year && !start.IsDecade) ? "День свадьбы" : (start.Year == null || start.IsDecade) ? "Годовщина" : (year - start.Year.Value) + "-ая годовщина"; yield return(new CalendarEventVM { Day = start.Day, Title = title, Type = CalendarEventType.Wedding, RelatedPage = rel.EventId == null ? new PageTitleExtendedVM { Title = "Свадьба", MainPhotoPath = "~/assets/img/unknown-event.svg" } : Map(context.Pages[rel.EventId.Value]), OtherPages = new [] { Map(context.Pages[rel.SourceId]), Map(context.Pages[rel.DestinationId]) } }); } }
/// <summary> /// Returns the relation groups with parents and siblings. /// </summary> private IEnumerable <RelationGroupVM> GetGroups(RelationContext ctx, Guid pageId, RelationDefinition[] defs) { var ids = new[] { pageId }; var relations = defs.Select(x => GetRelationVM(ctx, x, ids)) .Where(x => x != null) .ToList(); if (relations.Any()) { yield return new RelationGroupVM { Relations = relations } } ; }
/// <summary> /// Adds information from the current page to the context. /// </summary> private void AugmentRelationContext(RelationContext context, Page page, string rawFacts) { var facts = ParseFacts(page.Type, rawFacts); var excerpt = new RelationContext.PageExcerpt { Id = page.Id, Key = page.Key, Type = page.Type, Title = page.Title, Gender = Parse <bool>("Bio.Gender", "IsMale"), BirthDate = Parse <FuzzyDate>("Birth.Date", "Value"), DeathDate = Parse <FuzzyDate>("Death.Date", "Value"), }; context.Augment(excerpt); T?Parse <T>(params string[] parts) where T : struct
/// <summary> /// Returns the events for a particular day (or fuzzy events for the month). /// </summary> public async Task <CalendarDayVM> GetDayEventsAsync(int year, int month, int?day) { var context = await RelationContext.LoadContextAsync(_db); var events = GetPageEvents(year, month, context) .Concat(GetRelationEvents(year, month, context)) .Where(x => x.Day == day) .ToList(); return(new CalendarDayVM { IsActive = true, Day = day, Date = new FuzzyDate(year, month, day), Events = events }); }
/// <summary> /// Returns the events to display for the current month. /// </summary> public async Task <CalendarMonthVM> GetMonthEventsAsync(int year, int month) { var context = await RelationContext.LoadContextAsync(_db); var range = GetDisplayedRange(year, month); var events = GetPageEvents(year, month, context) .Concat(GetRelationEvents(year, month, context)) .ToList(); return(new CalendarMonthVM { Month = month, Year = year, Title = new FuzzyDate(year, month, null).ReadableDate.Capitalize(), Weeks = GetMonthGrid(range.from, range.to, month, events), FuzzyEvents = events.Where(x => x.Day == null).ToList() }); }
/// <summary> /// Checks the context for contradictory facts. /// </summary> public void Validate(RelationContext context, Guid[] updatedPageIds = null) { CheckLifespans(context); CheckWeddings(context); CheckParents(context); CheckParentLifespans(context); if (updatedPageIds != null) { foreach (var pageId in updatedPageIds) { if (pageId != Guid.Empty) { CheckLoops(context, pageId); } } } }
/// <summary> /// Checks the context for inconsistencies with lifespans of parents/children. /// </summary> private void CheckParentLifespans(RelationContext context) { foreach (var rel in context.Relations.Values.SelectMany(x => x)) { if (rel.Type != RelationType.Child) { continue; } var parent = context.Pages[rel.SourceId]; var child = context.Pages[rel.DestinationId]; if (parent.BirthDate >= child.BirthDate) { Error("Родитель не может быть старше ребенка", parent.Id, child.Id); } } }
static void Main(string[] args) { RelationContext context = new RelationContext(); int count = context.Medlemmar.Count(); /*if (count == 0) * AddData(context);*/ Console.WriteLine("** Sportklubben **"); if (context.Medlemmar.Count() > 0) { Console.WriteLine("Följande medlemmar finns:"); foreach (MedlemModel m in context.Medlemmar) { Console.WriteLine("\t" + m.Namn); } } if (context.Lag.Count() > 0) { Console.WriteLine("Följande lag finns:"); foreach (LagModel l in context.Lag) { Console.WriteLine("\t" + l.Namn);// + ", " + l.Sport); } } if (context.Matcher.Count() > 0) { Console.WriteLine("Följande matcher har spelats:"); foreach (MatchModel m in context.Matcher) { Console.WriteLine("\t" + m.Date.ToShortDateString() + "\t" + m.Lag1.Namn + " mot " + m.Lag2.Namn); foreach (MedlemModel medlem in m.Spelare) { Console.WriteLine("\t\tSpelare: " + medlem.Namn + " för laget " + medlem.Lag.Namn); } } } Console.Write("Program done, press any key to exit"); Console.ReadKey(); }
/// <summary> /// Loads all pages and groups them into subgraphs by relations. /// </summary> private IReadOnlyList <TreeLayoutVM> GetAllSubtrees(RelationContext ctx) { var excluded = new HashSet <Guid>(); var result = new List <TreeLayoutVM>(); while (true) { var root = ctx.Pages.Keys.FirstOrDefault(x => !excluded.Contains(x)); if (root == Guid.Empty) { break; } result.Add(GetSubtree(ctx, excluded, root)); } return(result); }
/// <summary> /// Validates the context integrity. /// </summary> public async Task ValidateAsync(IReadOnlyList <Relation> relations) { var firstRel = relations?.FirstOrDefault(); if (firstRel == null) { throw new ArgumentNullException(); } var context = await RelationContext.LoadContextAsync(_db); foreach (var rel in relations) { context.Augment(CreateExcerpt(rel)); } var core = new ValidatorCore(); core.Validate(context, new [] { firstRel.SourceId, firstRel.DestinationId, firstRel.EventId ?? Guid.Empty }); core.ThrowIfInvalid(context, nameof(RelationEditorVM.DestinationId)); }
/// <summary> /// Finds loops of a particular relation in the relation graph. /// </summary> private void CheckLoops(RelationContext context, Guid pageId) { var isLoopFound = false; var visited = context.Pages.ToDictionary(x => x.Key, x => false); CheckLoopsInternal(pageId); void CheckLoopsInternal(Guid id) { if (isLoopFound || !context.Relations.ContainsKey(id)) { return; } visited[id] = true; foreach (var rel in context.Relations[id]) { if (rel.Type != RelationType.Parent) { continue; } if (isLoopFound) { return; } if (visited[rel.DestinationId]) { isLoopFound = true; Error("Два человека не могут быть родителями друг для друга", rel.DestinationId, pageId); return; } CheckLoopsInternal(rel.DestinationId); } } }
public static QsiExpressionNode VisitRelation(RelationContext context) { switch (context) { case LogicalExpr1Context logicalExpr1: return(VisitLogicalExpr1(logicalExpr1)); case LikeExprContext likeExpr: return(VisitLikeExpr(likeExpr)); case IsNotNulExprContext isNotNulExpr: return(VisitIsNotNulExpr(isNotNulExpr)); case TokenExprContext tokenExpr: return(VisitTokenExpr(tokenExpr)); case InExpr1Context inExpr1: return(VisitInExpr1(inExpr1)); case InExpr2Context inExpr2: return(VisitInExpr2(inExpr2)); case ContainsExprContext constainsExpr: return(VisitContainsExpr(constainsExpr)); case LogicalExpr2Context logicalExpr2: return(VisitLogicalExpr2(logicalExpr2)); case TupleExprContext tupleExpr: return(VisitTupleExpr(tupleExpr)); case GroupExprContext groupExpr: return(VisitGroupExpr(groupExpr)); default: throw TreeHelper.NotSupportedTree(context); } }
/// <summary> /// Throws a ValidationException if there any violations found. /// </summary> public void ThrowIfInvalid(RelationContext context, string propName) { if (!Errors.Any()) { return; } var msgs = new List <KeyValuePair <string, string> >(); foreach (var err in Errors) { var msg = err.Message; if (err.PageIds?.Length > 0) { var pages = err.PageIds.Select(x => context.Pages[x].Title); msg += $" ({string.Join(", ", pages)})"; } msgs.Add(new KeyValuePair <string, string>(propName, msg)); } throw new ValidationException(msgs); }
/// <summary> /// Returns the groups for each spouse-based family. /// </summary> private IEnumerable <RelationGroupVM> GetSpouseGroups(RelationContext ctx, Guid pageId) { if (!ctx.Pages.TryGetValue(pageId, out var page)) { yield break; } if (page.Type != PageType.Person && page.Type != PageType.Pet) { yield break; } if (!ctx.Relations.ContainsKey(pageId)) { yield break; } var spouses = ctx.Relations[pageId] .Where(x => x.Type == RelationType.Spouse) .OrderBy(x => x.Duration); foreach (var spouse in spouses) { var ids = new[] { pageId, spouse.DestinationId }; var relations = SpouseDefinitions.Select(x => GetRelationVM(ctx, x, ids)) .Where(x => x != null) .ToList(); if (relations.Any()) { yield return new RelationGroupVM { Relations = relations } } ; } }
/// <summary> /// Returns the entire tree. /// </summary> public async Task <TreeVM> GetTreeAsync(Guid rootId) { var context = await RelationContext.LoadContextAsync(_db, new RelationContextOptions { PeopleOnly = true }); var parents = new HashSet <string>(); var persons = new Dictionary <Guid, TreePersonVM>(); var relations = new Dictionary <string, TreeRelationVM>(); var pending = new Queue <Guid>(); pending.Enqueue(rootId); while (pending.TryDequeue(out var currId)) { if (persons.ContainsKey(currId)) { continue; } if (!context.Pages.TryGetValue(currId, out var page)) { continue; } persons.Add( page.Id, new TreePersonVM { Id = page.Id.ToString(), Name = page.Title, MaidenName = page.MaidenName, Birth = page.BirthDate?.ShortReadableDate, Death = page.DeathDate?.ShortReadableDate, IsMale = page.Gender ?? true, IsDead = page.IsDead, Photo = GetPhoto(page.MainPhotoPath, page.Gender ?? true), Url = _url.Action("Description", "Page", new { area = "Front", key = page.Key }), Parents = GetParentRelationshipId(page) } ); if (context.Relations.TryGetValue(currId, out var rels)) { foreach (var rel in rels) { if (rel.Type != RelationType.Child && rel.Type != RelationType.Parent && rel.Type != RelationType.Spouse) { continue; } pending.Enqueue(rel.DestinationId); if (rel.Type == RelationType.Spouse) { AddRelationship(page.Id, rel.DestinationId); } } } } return(new TreeVM { Root = rootId.ToString(), Persons = persons.Values.OrderBy(x => x.Name).ToList(), Relations = relations.Values.OrderBy(x => x.Id).ToList() }); string GetParentRelationshipId(RelationContext.PageExcerpt page) { if (!context.Relations.TryGetValue(page.Id, out var allRels)) { return(null); } var rels = allRels.Where(x => x.Type == RelationType.Parent).ToList(); if (rels.Count == 0) { return(null); } var relKey = rels.Count == 1 ? rels[0].DestinationId + ":unknown" : rels.Select(x => x.DestinationId.ToString()).OrderBy(x => x).JoinString(":"); if (!parents.Contains(relKey)) { if (rels.Count == 1) { var fakeId = Guid.NewGuid(); var relPage = context.Pages[rels[0].DestinationId]; var fakeGender = !(relPage.Gender ?? true); persons.Add(fakeId, new TreePersonVM { Id = fakeId.ToString(), Name = "Неизвестно", IsMale = fakeGender, Photo = GetPhoto(null, fakeGender) }); AddRelationship(rels[0].DestinationId, fakeId, relKey); } else { AddRelationship(rels[0].DestinationId, rels[1].DestinationId); } parents.Add(relKey); } return(relKey); } void AddRelationship(Guid r1, Guid r2, string keyOverride = null) { var from = r1.ToString(); var to = r2.ToString(); if (from.CompareTo(to) >= 1) { var tmp = from; from = to; to = tmp; } var key = keyOverride; if (string.IsNullOrEmpty(key)) { key = from + ":" + to; } if (relations.ContainsKey(key)) { return; } relations.Add(key, new TreeRelationVM { Id = key, From = from, To = to }); } }
/// <summary> /// Returns a relation for all pages matching the definition. /// </summary> private RelationVM GetRelationVM(RelationContext ctx, RelationDefinition def, params Guid[] guids) { // Performs one step from the current page along the relation path and returns matching pages IEnumerable <RelationTarget> Step(RelationTarget elem, RelationPathSegment segment, Guid?guidFilter) { if (!ctx.Relations.TryGetValue(elem.Page.Id, out var rels)) { return(Enumerable.Empty <RelationTarget>()); } return(from rel in rels where rel.Type == segment.Type where guidFilter == null || rel.DestinationId == guidFilter let page = ctx.Pages[rel.DestinationId] where segment.Gender == null || segment.Gender == page.Gender where !elem.VisitedPages.Contains(page) select new RelationTarget(page, rel, elem.VisitedPages.Append(page))); } // Finds pages matching the entire path from current page IEnumerable <RelationTarget> GetMatchingPages(RelationPath path) { if (!ctx.Pages.TryGetValue(guids[0], out var root)) { return(Array.Empty <RelationTarget>()); } var currents = new List <RelationTarget> { new RelationTarget(root, null, new SinglyLinkedList <RelationContext.PageExcerpt>(root)) }; for (var depth = 0; depth < path.Segments.Count; depth++) { if (currents.Count == 0) { break; } var segment = path.Segments[depth]; var guidFilter = path.IsBound && (depth + 1) < guids.Length ? guids[depth + 1] : (Guid?)null; currents = currents.Select(x => Step(x, segment, guidFilter)) .SelectMany(x => x) .ToList(); } return(currents); } // Gets the range to display alongside the relation FuzzyRange?GetRange(RelationTarget elem) { if (def.DurationDisplayMode == RelationDurationDisplayMode.RelationRange) { return(elem.Relation.Duration); } if (def.DurationDisplayMode == RelationDurationDisplayMode.Birth) { if (elem.Page.BirthDate != null) { return(new FuzzyRange(elem.Page.BirthDate, null)); } } if (def.DurationDisplayMode == RelationDurationDisplayMode.Life) { if (elem.Page.BirthDate != null || elem.Page.DeathDate != null) { return(new FuzzyRange(elem.Page.BirthDate, elem.Page.DeathDate)); } } return(null); } PageTitleVM GetEventPageTitle(Guid?eventId) { if (eventId == null) { return(null); } var page = ctx.Pages[eventId.Value]; return(new PageTitleVM { Title = page.Title, Key = page.Key }); } var posPaths = def.Paths.Where(x => !x.IsExcluded); var negPaths = def.Paths.Where(x => x.IsExcluded); // A+B-C means: all pages matching both paths A & B, but not matching path C var results = posPaths.Select(GetMatchingPages) .Aggregate((a, b) => a.Intersect(b)) .Except(negPaths.Select(GetMatchingPages) .SelectMany(x => x)) .ToList(); if (!results.Any()) { return(null); } return(new RelationVM { Title = def.GetName(results.Count, results[0].Page.Gender), Pages = results.OrderBy(x => x.Page.BirthDate) .Select(elem => new RelatedPageVM { Title = StringHelper.Coalesce(elem.Page.ShortName, elem.Page.Title), Key = elem.Page.Key, Duration = GetRange(elem), RelationEvent = GetEventPageTitle(elem.Relation.EventId) }) .ToList() }); }
/// <summary> /// Infers relation-based events for the current month. /// </summary> private IEnumerable <CalendarEventVM> GetRelationEvents(int year, int month, RelationContext context) { var visited = new HashSet <string>(); var maxDate = new FuzzyDate(CreateDate(year, month).AddMonths(1).AddSeconds(-1)); foreach (var rel in context.Relations.SelectMany(x => x.Value)) { if (rel.Duration is not FuzzyRange duration || duration.RangeStart is not FuzzyDate start || start.Month != month) { continue; } if (duration.RangeEnd is FuzzyDate end && end <= maxDate) { continue; } var hash = string.Concat(rel.SourceId.ToString(), rel.DestinationId.ToString(), duration.ToString()); if (visited.Contains(hash)) { continue; } var inverseHash = string.Concat(rel.DestinationId.ToString(), rel.SourceId.ToString(), duration.ToString()); visited.Add(hash); visited.Add(inverseHash); var evt = rel.Type switch { RelationType.Spouse => GetWeddingEvent(rel, start), RelationType.Owner or RelationType.Pet => GetPetAdoptionEvent(start), RelationType.StepChild or RelationType.StepParent => GetChildAdoptionEvent(rel, start), _ => null }; if (evt != null) { evt.Day = start.Day; evt.OtherPages = new[] { Map(context.Pages[rel.SourceId]), Map(context.Pages[rel.DestinationId]) }; yield return(evt); } } CalendarEventVM GetWeddingEvent(RelationContext.RelationExcerpt rel, FuzzyDate start) { var title = (year == start.Year && !start.IsDecade) ? "День свадьбы" : (start.Year == null || start.IsDecade) ? "Годовщина" : (year - start.Year.Value) + "-ая годовщина"; return(new CalendarEventVM { Title = title, Type = CalendarEventType.Wedding, RelatedPage = rel.EventId == null ? new PageTitleExtendedVM { Title = "Свадьба" } : Map(context.Pages[rel.EventId.Value]), }); } CalendarEventVM GetPetAdoptionEvent(FuzzyDate start) { if (year != start.Year || start.IsDecade) { return(null); } return(new CalendarEventVM { Title = "Появление питомца", Type = CalendarEventType.PetAdoption, RelatedPage = new PageTitleExtendedVM { Title = "Событие" } }); } CalendarEventVM GetChildAdoptionEvent(RelationContext.RelationExcerpt rel, FuzzyDate start) { if (year != start.Year || start.IsDecade) { return(null); } var child = context.Pages[ rel.Type == RelationType.StepChild ? rel.SourceId : rel.DestinationId ]; var title = child.Gender == false ? "Удочерение" : "Усыновление"; return(new CalendarEventVM { Title = title, Type = CalendarEventType.ChildAdoption, RelatedPage = new PageTitleExtendedVM { Title = "Событие" } }); } }
private static void AddData(RelationContext context) { MedlemModel m1 = new MedlemModel() { Namn = "Anna" }; // fotboll MedlemModel m2 = new MedlemModel() { Namn = "Bertil" }; // fotboll MedlemModel m3 = new MedlemModel() { Namn = "Cecilia" }; // biljard MedlemModel m4 = new MedlemModel() { Namn = "David" }; // biljard LagModel lag1 = new LagModel() { Namn = "Bara boll", //Sport = LagModel.SportTyp.Fotboll, Medlemmar = new List <MedlemModel>() }; LagModel lag2 = new LagModel() { Namn = "Rulla rakt", //Sport = LagModel.SportTyp.Biljard, Medlemmar = new List <MedlemModel>() }; MatchModel match1 = new MatchModel() { Date = new DateTime(2016, 4, 30) }; MatchModel match2 = new MatchModel() { Date = new DateTime(2016, 5, 1) }; m1.Lag = lag1; m2.Lag = lag1; m3.Lag = lag2; m4.Lag = lag2; lag1.Medlemmar.Add(m1); lag1.Medlemmar.Add(m2); lag2.Medlemmar.Add(m3); lag2.Medlemmar.Add(m4); match1.Lag1 = lag1; match1.Lag2 = lag2; match1.Spelare.Add(m1); match1.Spelare.Add(m3); match2.Lag1 = lag1; match2.Lag2 = lag2; match2.Spelare.Add(m2); match2.Spelare.Add(m4); context.Medlemmar.Add(m1); context.Medlemmar.Add(m2); context.Medlemmar.Add(m3); context.Medlemmar.Add(m4); context.Lag.Add(lag1); context.Lag.Add(lag2); context.Matcher.Add(match1); context.Matcher.Add(match2); context.SaveChanges(); }
/// <summary> /// Returns the list of changeset groups at given offset. /// </summary> public async IAsyncEnumerable <ChangesetEventVM> GetEventsAsync(int page = 0) { const int PAGE_SIZE = 20; var groups = await _db.ChangeEvents .OrderByDescending(x => x.Date) .Skip(PAGE_SIZE * page) .Take(PAGE_SIZE) .ToListAsync(); var parsedGroups = groups.Select(x => new { x.GroupKey, Ids = x.Ids.Split(',').Select(y => y.Parse <Guid>()).ToList() }) .ToList(); var changeIds = parsedGroups.SelectMany(x => x.Ids).ToList(); var changes = await _db.Changes .AsNoTracking() .Include(x => x.EditedMedia) .Include(x => x.EditedPage) .Include(x => x.EditedRelation.Destination) .Include(x => x.EditedRelation.Source) .Include(x => x.Author) .Where(x => changeIds.Contains(x.Id)) .ToDictionaryAsync(x => x.Id, x => x); // todo: cache genders in the user table to avoid loading the entire context var ctx = await RelationContext.LoadContextAsync(_db, new RelationContextOptions { PagesOnly = true, PeopleOnly = true }); foreach (var group in parsedGroups) { var chg = changes[group.Ids.First()]; var vm = _mapper.Map <ChangesetEventVM>(chg); if (vm.User.PageId != null) { vm.User.IsMale = ctx.Pages[vm.User.PageId.Value].Gender; } if (chg.Type == ChangesetEntityType.Page) { vm.MainLink = GetLinkToPage(chg.EditedPage); vm.ElementCount = 1; } else if (chg.Type == ChangesetEntityType.Relation) { var rel = chg.EditedRelation; vm.MainLink = new LinkVM { Title = chg.EditedRelation.Type.GetEnumDescription(), Url = _url.Action("Update", "Relations", new { area = "Admin", id = rel.Id }) }; vm.ExtraLinks = new[] { GetLinkToPage(rel.Destination), GetLinkToPage(rel.Source) }; vm.ElementCount = 1; } else if (chg.Type == ChangesetEntityType.Media) { vm.ElementCount = group.Ids.Count; vm.MediaThumbnails = group.Ids .Take(50) .Select(x => changes[x].EditedMedia) .Where(x => File.Exists(_env.GetMediaPath(x))) .Select(x => new MediaThumbnailVM { Key = x.Key, Type = x.Type, ThumbnailUrl = MediaPresenterService.GetSizedMediaPath(x.FilePath, MediaSize.Small), }) .ToList(); } yield return(vm); } }