コード例 #1
0
ファイル: ValidatorCore.cs プロジェクト: vitaut7/bonsai
        /// <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);
                    }
                }
            }
        }
コード例 #2
0
        /// <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());
        }
コード例 #3
0
ファイル: ValidatorCore.cs プロジェクト: vitaut7/bonsai
        /// <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);
                    }
                }
            }
        }
コード例 #4
0
ファイル: TreeLayoutService.cs プロジェクト: hbrtv/bonsai
        /// <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);
        }
コード例 #5
0
ファイル: ValidatorCore.cs プロジェクト: vitaut7/bonsai
 /// <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);
         }
     }
 }
コード例 #6
0
        /// <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));
        }
コード例 #7
0
        /// <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)
                        });
                    }
                }
            }
        }
コード例 #8
0
        /// <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])
                    }
                });
            }
        }
コード例 #9
0
        /// <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
                }
            }
            ;
        }
コード例 #10
0
        /// <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
コード例 #11
0
        /// <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
            });
        }
コード例 #12
0
        /// <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()
            });
        }
コード例 #13
0
ファイル: ValidatorCore.cs プロジェクト: vitaut7/bonsai
        /// <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);
                    }
                }
            }
        }
コード例 #14
0
ファイル: ValidatorCore.cs プロジェクト: vitaut7/bonsai
        /// <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);
                }
            }
        }
コード例 #15
0
ファイル: Program.cs プロジェクト: lejonmanen/EfDemo160502
        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();
        }
コード例 #16
0
ファイル: TreeLayoutService.cs プロジェクト: hbrtv/bonsai
        /// <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);
        }
コード例 #17
0
        /// <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));
        }
コード例 #18
0
ファイル: ValidatorCore.cs プロジェクト: vitaut7/bonsai
        /// <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);
                }
            }
        }
コード例 #19
0
        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);
            }
        }
コード例 #20
0
ファイル: ValidatorCore.cs プロジェクト: vitaut7/bonsai
        /// <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);
        }
コード例 #21
0
        /// <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
                    }
                }
                ;
            }
        }
コード例 #22
0
        /// <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
                });
            }
        }
コード例 #23
0
        /// <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()
            });
        }
コード例 #24
0
        /// <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 = "Событие"
                    }
                });
            }
        }
コード例 #25
0
ファイル: Program.cs プロジェクト: lejonmanen/EfDemo160502
        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();
        }
コード例 #26
0
        /// <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);
            }
        }