/// <summary>
        /// Initializes a new <see cref="AutoAnimeGroupCalculator"/> instance.
        /// </summary>
        /// <param name="relationMap">A <see cref="ILookup{TKey,TElement}"/> that maps anime IDs to their relations.</param>
        /// <param name="exclusions">The relation/anime types to ignore when building relation graphs.</param>
        /// <exception cref="ArgumentNullException"><paramref name="relationMap"/> is <c>null</c>.</exception>
        public AutoAnimeGroupCalculator(ILookup <int, AnimeRelation> relationMap,
                                        AutoGroupExclude exclusions = AutoGroupExclude.SameSetting | AutoGroupExclude.Character,
                                        AnimeRelationType relationsToFuzzyTitleTest           = AnimeRelationType.SecondaryRelations,
                                        MainAnimeSelectionStrategy mainAnimeSelectionStrategy = MainAnimeSelectionStrategy.MinAirDate)
        {
            if (relationMap == null)
            {
                throw new ArgumentNullException(nameof(relationMap));
            }

            _relationMap = relationMap;
            _exclusions  = exclusions;
            _relationsToFuzzyTitleTest = relationsToFuzzyTitleTest;

            switch (mainAnimeSelectionStrategy)
            {
            case MainAnimeSelectionStrategy.MinAirDate:
                _mainAnimeSelector = FindSuitableAnimeByMinAirDate;
                break;

            case MainAnimeSelectionStrategy.Weighted:
                _mainAnimeSelector = FindSuitableAnimeByWeighting;
                break;
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Creates a new <see cref="AutoAnimeGroupCalculator"/> using relationships stored in the database.
        /// </summary>
        /// <param name="session">The NHibernate session.</param>
        /// <returns>The created <see cref="AutoAnimeGroupCalculator"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="session"/> is <c>null</c>.</exception>
        public static AutoAnimeGroupCalculator CreateFromServerSettings(ISessionWrapper session)
        {
            string                     exclusionsSetting          = ServerSettings.Instance.AutoGroupSeriesRelationExclusions;
            AutoGroupExclude           exclusions                 = AutoGroupExclude.None;
            AnimeRelationType          relationsToFuzzyTitleTest  = AnimeRelationType.None;
            MainAnimeSelectionStrategy mainAnimeSelectionStrategy = ServerSettings.Instance.AutoGroupSeriesUseScoreAlgorithm
                ? MainAnimeSelectionStrategy.Weighted
                : MainAnimeSelectionStrategy.MinAirDate;

            if (!string.IsNullOrEmpty(exclusionsSetting))
            {
                var exclusionTokens = exclusionsSetting
                                      .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
                                      .Select(s => s.Trim())
                                      .Where(s => s.Length > 0)
                                      .ToList();

                exclusions = exclusionTokens
                             .Select(s =>
                {
                    s = s.Replace(" ", string.Empty);
                    Enum.TryParse(s, true, out AutoGroupExclude exclude);

                    return(exclude);
                })
                             .Aggregate(AutoGroupExclude.None, (exclude, allExcludes) => allExcludes | exclude);

                if (exclusionTokens.Contains("AllowDissimilarTitleExclusion", StringComparer.OrdinalIgnoreCase))
                {
                    relationsToFuzzyTitleTest = AnimeRelationType.SecondaryRelations;
                }
            }

            return(Create(session, exclusions, relationsToFuzzyTitleTest, mainAnimeSelectionStrategy));
        }
Ejemplo n.º 3
0
            public RelationEdge(AnimeRelationType relationType, int fromAnimeId, int toAnimeId)
            {
                // Normalize the relationship so that the directions are consistent (e.g. Prequel/FullStory/ParentStory will
                // always be on the FROM size, and Sequel/Summary/SideStory will always be on the TO side).
                // Doing this means that when added to a HashSet, etc. we'll filter out duplicate/reversed relations
                switch (relationType)
                {
                case AnimeRelationType.Prequel:
                    AnimeId1      = fromAnimeId;
                    RelationType1 = AnimeRelationType.Prequel;
                    AnimeId2      = toAnimeId;
                    RelationType2 = AnimeRelationType.Sequel;
                    break;

                case AnimeRelationType.Sequel:
                    AnimeId1      = toAnimeId;
                    RelationType1 = AnimeRelationType.Prequel;
                    AnimeId2      = fromAnimeId;
                    RelationType2 = AnimeRelationType.Sequel;
                    break;

                case AnimeRelationType.FullStory:
                    AnimeId1      = fromAnimeId;
                    RelationType1 = AnimeRelationType.FullStory;
                    AnimeId2      = toAnimeId;
                    RelationType2 = AnimeRelationType.Summary;
                    break;

                case AnimeRelationType.Summary:
                    AnimeId1      = toAnimeId;
                    RelationType1 = AnimeRelationType.FullStory;
                    AnimeId2      = fromAnimeId;
                    RelationType2 = AnimeRelationType.Summary;
                    break;

                case AnimeRelationType.ParentStory:
                    AnimeId1      = fromAnimeId;
                    RelationType1 = AnimeRelationType.ParentStory;
                    AnimeId2      = toAnimeId;
                    RelationType2 = AnimeRelationType.SideStory;
                    break;

                case AnimeRelationType.SideStory:
                    AnimeId1      = toAnimeId;
                    RelationType1 = AnimeRelationType.ParentStory;
                    AnimeId2      = fromAnimeId;
                    RelationType2 = AnimeRelationType.SideStory;
                    break;

                default:
                    AnimeId1      = Math.Min(fromAnimeId, toAnimeId);
                    RelationType1 = relationType;
                    AnimeId2      = Math.Max(fromAnimeId, toAnimeId);
                    RelationType2 = relationType;
                    break;
                }
            }
Ejemplo n.º 4
0
        /// <summary>
        /// Initializes a new <see cref="AutoAnimeGroupCalculator"/> instance.
        /// </summary>
        /// <param name="relationMap">A <see cref="ILookup{TKey,TElement}"/> that maps anime IDs to their relations.</param>
        /// <param name="exclusions">The relation/anime types to ignore when building relation graphs.</param>
        /// <param name="relationsToFuzzyTitleTest">The relationships for which we'll perform title similarity checks for
        /// (If the titles aren't similar enough then the anime will end up in different groups).</param>
        /// <param name="mainAnimeSelectionStrategy">The strategy to use for selecting the "main" anime that will be used
        /// for representing the group.</param>
        /// <exception cref="ArgumentNullException"><paramref name="relationMap"/> is <c>null</c>.</exception>
        public AutoAnimeGroupCalculator(Dictionary <int, List <AnimeRelation> > relationMap, AutoGroupExclude exclusions,
                                        AnimeRelationType relationsToFuzzyTitleTest, MainAnimeSelectionStrategy mainAnimeSelectionStrategy)
        {
            if (relationMap == null)
            {
                throw new ArgumentNullException(nameof(relationMap));
            }

            _relationMap = relationMap;
            _exclusions  = exclusions;
            _relationsToFuzzyTitleTest = relationsToFuzzyTitleTest;

            switch (mainAnimeSelectionStrategy)
            {
            case MainAnimeSelectionStrategy.MinAirDate:
                _mainAnimeSelector = FindSuitableAnimeByMinAirDate;
                break;

            case MainAnimeSelectionStrategy.Weighted:
                _mainAnimeSelector = FindSuitableAnimeByWeighting;
                break;
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Creates a new <see cref="AutoAnimeGroupCalculator"/> using relationships stored in the database.
        /// </summary>
        /// <param name="session">The NHibernate session.</param>
        /// <param name="exclusions">The relation/anime types to ignore when building relation graphs.</param>
        /// <param name="relationsToFuzzyTitleTest">The relationships for which we'll perform title similarity checks for
        /// (If the titles aren't similar enough then the anime will end up in different groups).</param>
        /// <param name="mainAnimeSelectionStrategy">The strategy to use for selecting the "main" anime that will be used
        /// for representing the group.</param>
        /// <returns>The created <see cref="AutoAnimeGroupCalculator"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="session"/> is <c>null</c>.</exception>
        public static AutoAnimeGroupCalculator Create(ISessionWrapper session,
                                                      AutoGroupExclude exclusions = AutoGroupExclude.SameSetting | AutoGroupExclude.Character,
                                                      AnimeRelationType relationsToFuzzyTitleTest           = AnimeRelationType.SecondaryRelations,
                                                      MainAnimeSelectionStrategy mainAnimeSelectionStrategy = MainAnimeSelectionStrategy.MinAirDate)
        {
            if (session == null)
            {
                throw new ArgumentNullException(nameof(session));
            }

            var relationshipMap = session.CreateSQLQuery(@"
                SELECT    fromAnime.AnimeID AS fromAnimeId
                        , toAnime.AnimeID AS toAnimeId
                        , fromAnime.AnimeType AS fromAnimeType
                        , toAnime.AnimeType AS toAnimeType
                        , fromAnime.MainTitle AS fromMainTitle
                        , toAnime.MainTitle AS toMainTitle
                        , fromAnime.AirDate AS fromAirDate
                        , toAnime.AirDate AS toAirDate
                        , rel.RelationType AS relationType
                    FROM AniDB_Anime_Relation rel
                        INNER JOIN AniDB_Anime fromAnime
                            ON fromAnime.AnimeID = rel.AnimeID
                        INNER JOIN AniDB_Anime toAnime
                            ON toAnime.AnimeID = rel.RelatedAnimeID")
                                  .AddScalar("fromAnimeId", NHibernateUtil.Int32)
                                  .AddScalar("toAnimeId", NHibernateUtil.Int32)
                                  .AddScalar("fromAnimeType", NHibernateUtil.Int32)
                                  .AddScalar("toAnimeType", NHibernateUtil.Int32)
                                  .AddScalar("fromMainTitle", NHibernateUtil.String)
                                  .AddScalar("toMainTitle", NHibernateUtil.String)
                                  .AddScalar("fromAirDate", NHibernateUtil.DateTime)
                                  .AddScalar("toAirDate", NHibernateUtil.DateTime)
                                  .AddScalar("relationType", NHibernateUtil.String)
                                  .List <object[]>()
                                  .Select(r =>
            {
                var relation = new AnimeRelation
                {
                    FromId        = (int)r[0],
                    ToId          = (int)r[1],
                    FromType      = (AnimeType)r[2],
                    ToType        = (AnimeType)r[3],
                    FromMainTitle = (string)r[4],
                    ToMainTitle   = (string)r[5],
                    FromAirDate   = (DateTime?)r[6],
                    ToAirDate     = (DateTime?)r[7]
                };

                switch (((string)r[8]).ToLowerInvariant())
                {
                case "full story":
                    relation.RelationType = AnimeRelationType.FullStory;
                    break;

                case "summary":
                    relation.RelationType = AnimeRelationType.Summary;
                    break;

                case "parent story":
                    relation.RelationType = AnimeRelationType.ParentStory;
                    break;

                case "side story":
                    relation.RelationType = AnimeRelationType.SideStory;
                    break;

                case "prequel":
                    relation.RelationType = AnimeRelationType.Prequel;
                    break;

                case "sequel":
                    relation.RelationType = AnimeRelationType.Sequel;
                    break;

                case "alternative setting":
                    relation.RelationType = AnimeRelationType.AlternateSetting;
                    break;

                case "alternative version":
                    relation.RelationType = AnimeRelationType.AlternateVersion;
                    break;

                case "same setting":
                    relation.RelationType = AnimeRelationType.SameSetting;
                    break;

                case "character":
                    relation.RelationType = AnimeRelationType.Character;
                    break;

                default:
                    relation.RelationType = AnimeRelationType.Other;
                    break;
                }

                return(relation);
            })
                                  .ToLookup(k => k.FromId);

            return(new AutoAnimeGroupCalculator(relationshipMap, exclusions, relationsToFuzzyTitleTest,
                                                mainAnimeSelectionStrategy));
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Creates a new <see cref="AutoAnimeGroupCalculator"/> using relationships stored in the database.
        /// </summary>
        /// <param name="exclusions">The relation/anime types to ignore when building relation graphs.</param>
        /// <param name="relationsToFuzzyTitleTest">The relationships for which we'll perform title similarity checks for
        /// (If the titles aren't similar enough then the anime will end up in different groups).</param>
        /// <param name="mainAnimeSelectionStrategy">The strategy to use for selecting the "main" anime that will be used
        /// for representing the group.</param>
        /// <returns>The created <see cref="AutoAnimeGroupCalculator"/>.</returns>
        public static AutoAnimeGroupCalculator Create(AutoGroupExclude exclusions = AutoGroupExclude.SameSetting | AutoGroupExclude.Character, AnimeRelationType relationsToFuzzyTitleTest = AnimeRelationType.SecondaryRelations, MainAnimeSelectionStrategy mainAnimeSelectionStrategy = MainAnimeSelectionStrategy.MinAirDate)
        {
            Dictionary <int, (int type, string title, DateTime?airdate)> animes = Repo.Instance.AniDB_Anime.GetRelationInfo();
            Dictionary <int, List <AnimeRelation> > relations = new Dictionary <int, List <AnimeRelation> >();

            foreach (AniDB_Anime_Relation rel in Repo.Instance.AniDB_Anime_Relation.GetAll())
            {
                (int type, string title, DateTime? airdate)from = animes.ContainsKey(rel.AnimeID) ? animes[rel.AnimeID] : (0, null, null);
                (int type, string title, DateTime? airdate)to   = animes.ContainsKey(rel.RelatedAnimeID) ? animes[rel.RelatedAnimeID] : (0, null, null);
                if (from.title != null && to.title != null)
                {
                    var relation = new AnimeRelation
                    {
                        FromId        = rel.AnimeID,
                        ToId          = rel.RelatedAnimeID,
                        FromType      = (AnimeType)from.type,
                        ToType        = (AnimeType)to.type,
                        FromMainTitle = from.title,
                        ToMainTitle   = to.title,
                        FromAirDate   = from.airdate,
                        ToAirDate     = to.airdate
                    };

                    switch ((rel.RelationType).ToLowerInvariant())
                    {
                    case "full story":
                        relation.RelationType = AnimeRelationType.FullStory;
                        break;

                    case "summary":
                        relation.RelationType = AnimeRelationType.Summary;
                        break;

                    case "parent story":
                        relation.RelationType = AnimeRelationType.ParentStory;
                        break;

                    case "side story":
                        relation.RelationType = AnimeRelationType.SideStory;
                        break;

                    case "prequel":
                        relation.RelationType = AnimeRelationType.Prequel;
                        break;

                    case "sequel":
                        relation.RelationType = AnimeRelationType.Sequel;
                        break;

                    case "alternative setting":
                        relation.RelationType = AnimeRelationType.AlternateSetting;
                        break;

                    case "alternative version":
                        relation.RelationType = AnimeRelationType.AlternateVersion;
                        break;

                    case "same setting":
                        relation.RelationType = AnimeRelationType.SameSetting;
                        break;

                    case "character":
                        relation.RelationType = AnimeRelationType.Character;
                        break;

                    default:
                        relation.RelationType = AnimeRelationType.Other;
                        break;
                    }
                    if (!relations.ContainsKey(rel.AnimeID))
                    {
                        relations.Add(rel.AnimeID, new List <AnimeRelation>());
                    }
                    relations[rel.AnimeID].Add(relation);
                }
            }
            return(new AutoAnimeGroupCalculator(relations, exclusions, relationsToFuzzyTitleTest, mainAnimeSelectionStrategy));
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Creates a new <see cref="AutoAnimeGroupCalculator"/> using relationships stored in the database.
        /// </summary>
        /// <param name="con">The open database connection.</param>
        /// <param name="exclusions">The relation/anime types to ignore when building relation graphs.</param>
        /// <param name="relationsToFuzzyTitleTest">The relationships for which we'll perform title similarity checks for
        /// (If the titles aren't similar enough then the anime will end up in different groups).</param>
        /// <param name="mainAnimeSelectionStrategy">The strategy to use for selecting the "main" anime that will be used
        /// for representing the group.</param>
        /// <returns>The created <see cref="AutoAnimeGroupCalculator"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="con"/> is <c>null</c>.</exception>
        public static AutoAnimeGroupCalculator Create(IDbConnection con, AutoGroupExclude exclusions = AutoGroupExclude.SameSetting | AutoGroupExclude.Character,
                                                      AnimeRelationType relationsToFuzzyTitleTest    = AnimeRelationType.SecondaryRelations, MainAnimeSelectionStrategy mainAnimeSelectionStrategy = MainAnimeSelectionStrategy.MinAirDate)
        {
            if (con == null)
            {
                throw new ArgumentNullException(nameof(con));
            }

            using (IDbCommand cmd = con.CreateCommand())
            {
                cmd.CommandText = @"
               SELECT  fromAnime.AnimeID AS fromAnimeId
                     , toAnime.AnimeID AS toAnimeId
                     , fromAnime.AnimeType AS fromAnimeType
                     , toAnime.AnimeType AS toAnimeType
                     , fromAnime.MainTitle AS fromMainTitle
                     , toAnime.MainTitle AS toMainTitle
                     , fromAnime.AirDate AS fromAirDate
                     , toAnime.AirDate AS toAirDate
                     , rel.RelationType AS relationType
                  FROM AniDB_Anime_Relation rel
                     INNER JOIN AniDB_Anime fromAnime
                        ON fromAnime.AnimeID = rel.AnimeID
                     INNER JOIN AniDB_Anime toAnime
                        ON toAnime.AnimeID = rel.RelatedAnimeID";

                using (IDataReader reader = cmd.ExecuteReader())
                {
                    var relationshipMap = ReadAnimeRelations(reader).ToLookup(k => k.FromId);

                    return(new AutoAnimeGroupCalculator(relationshipMap, exclusions, relationsToFuzzyTitleTest,
                                                        mainAnimeSelectionStrategy));
                }
            }

            IEnumerable <AnimeRelation> ReadAnimeRelations(IDataReader reader)
            {
                while (reader.Read())
                {
                    var relation = new AnimeRelation
                    {
                        FromId        = reader.GetInt32(0),
                        ToId          = reader.GetInt32(1),
                        FromType      = (AnimeType)reader.GetInt32(2),
                        ToType        = (AnimeType)reader.GetInt32(3),
                        FromMainTitle = reader.GetString(4),
                        ToMainTitle   = reader.GetString(5),
                        FromAirDate   = reader.IsDBNull(6) ? null : (DateTime?)reader.GetDateTime(6),
                        ToAirDate     = reader.IsDBNull(7) ? null : (DateTime?)reader.GetDateTime(7)
                    };

                    switch ((reader.GetString(8)).ToLowerInvariant())
                    {
                    case "full story":
                        relation.RelationType = AnimeRelationType.FullStory;
                        break;

                    case "summary":
                        relation.RelationType = AnimeRelationType.Summary;
                        break;

                    case "parent story":
                        relation.RelationType = AnimeRelationType.ParentStory;
                        break;

                    case "side story":
                        relation.RelationType = AnimeRelationType.SideStory;
                        break;

                    case "prequel":
                        relation.RelationType = AnimeRelationType.Prequel;
                        break;

                    case "sequel":
                        relation.RelationType = AnimeRelationType.Sequel;
                        break;

                    case "alternative setting":
                        relation.RelationType = AnimeRelationType.AlternateSetting;
                        break;

                    case "alternative version":
                        relation.RelationType = AnimeRelationType.AlternateVersion;
                        break;

                    case "same setting":
                        relation.RelationType = AnimeRelationType.SameSetting;
                        break;

                    case "character":
                        relation.RelationType = AnimeRelationType.Character;
                        break;

                    default:
                        relation.RelationType = AnimeRelationType.Other;
                        break;
                    }

                    yield return(relation);
                }
            }
        }