Example #1
0
        /// <summary>
        /// Determines whether or not we should consider using the specified <see cref="AnimeRelation"/>.
        /// </summary>
        /// <remarks>
        /// Relationships such as Prequel/Sequel, Full Story/Summary, Parent Story/Side Story, are automatically
        /// considered (unless in exclusion list). However, if fuzzy title testing is enabled then the other relationship types
        /// are only considered if their main titles "fuzzily" match.
        /// </remarks>
        /// <param name="rel">The <see cref="AnimeRelation"/> to test.</param>
        /// <returns><c>true</c> if the specified <see cref="AnimeRelation"/> should be considered when building
        /// the group graph; otherwise, <c>false</c>.</returns>
        private bool ShouldConsiderAnimeRelation(AnimeRelation rel)
        {
            if (((int)rel.RelationType & (int)_exclusions) != 0)
            {
                return(false); // The relation is in the exclusion list, so ignore it
            }
            // Check if we are excluding Movies or OVAs
            if ((_exclusions & AutoGroupExclude.Movie) == AutoGroupExclude.Movie &&
                (rel.FromType == AnimeType.Movie || rel.ToType == AnimeType.Movie) ||
                (_exclusions & AutoGroupExclude.Ova) == AutoGroupExclude.Ova &&
                (rel.FromType == AnimeType.OVA || rel.ToType == AnimeType.OVA))
            {
                return(false);
            }
            // Are we configured to do a fuzzy title test for this particular relation type? If not, then the relation is immediately allowed
            if ((rel.RelationType & _relationsToFuzzyTitleTest) == 0)
            {
                return(true);
            }

            // Perform a very poor man's string metric test.
            // We split both titles up into tokens (basically words) and count the number of times each word
            // appears in both titles (we'll also count characters of matching words).
            // The more words/characters that match, the more likely we'll consider the relationship
            string[] fromTitleTokens = CreateTokensFromTitle(rel.FromMainTitle);
            string[] toTitleTokens   = CreateTokensFromTitle(rel.ToMainTitle);
            int      matchLen        = 0;
            int      matches         = 0;

            foreach (string fromToken in fromTitleTokens)
            {
                foreach (string toToken in toTitleTokens)
                {
                    if (fromToken.Equals(toToken, StringComparison.InvariantCultureIgnoreCase))
                    {
                        matchLen += fromToken.Length;
                        matches++;
                        break;
                    }
                }
            }

            if (matches == 0)
            {
                return(false);
            }

            int shortestTitleLen = Math.Min(fromTitleTokens.Sum(s => s.Length), toTitleTokens.Sum(s => s.Length));
            int minTokenCount    = Math.Min(fromTitleTokens.Length, toTitleTokens.Length);

            // Either approximately half the words must match,
            // or the total length of the matched words must equate to 40% or more of the shortest title
            return((matches >= minTokenCount / 2) || matchLen >= Math.Max(1, (int)(shortestTitleLen * 0.4)));
        }
Example #2
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));
        }
Example #3
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));
        }
Example #4
0
        private static ILookup <int, AnimeRelation> LoadRelations(IDbConnection con)
        {
            using (IDbCommand cmd = con.CreateCommand())
            {
                cmd.CommandText = @"
                    SELECT fromAnime.AnimeID, toAnime.AnimeID, fromAnime.AnimeType, toAnime.AnimeType, fromAnime.MainTitle, toAnime.MainTitle, rel.RelationType, fromAnime.AirDate, toAnime.AirDate
                        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 relationMap = reader.EnumerateRecords()
                                      .Select(r =>
                    {
                        var relation = new AnimeRelation
                        {
                            FromId        = r.GetInt32(0),
                            ToId          = r.GetInt32(1),
                            FromType      = (AnimeTypes)r.GetInt32(2),
                            ToType        = (AnimeTypes)r.GetInt32(3),
                            FromMainTitle = r.GetString(4),
                            ToMainTitle   = r.GetString(5),
                            FromAirDate   = r.IsDBNull(7) ? (DateTime?)null : r.GetDateTime(7),
                            ToAirDate     = r.IsDBNull(8) ? (DateTime?)null : r.GetDateTime(8)
                        };

                        switch (r.GetString(6).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.AlternativeSetting;
                            break;

                        case "alternative version":
                            relation.RelationType = AnimeRelationType.AlternativeVersion;
                            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(r => r.FromId);

                    return(relationMap);
                }
            }
        }
Example #5
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);
                }
            }
        }