예제 #1
0
        private MalTrainingData FilterOutSpecialsAndTrim(MalTrainingData trainingData)
        {
            Dictionary <int, MalUserListEntries> filteredUsers = new Dictionary <int, MalUserListEntries>();

            foreach (int userId in trainingData.Users.Keys.Take(m_recommender.NumRecommenders))
            {
                List <ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId> filteredEntryList = new List <ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId>();
                foreach (int animeId in trainingData.Users[userId].Entries.Keys)
                {
                    if (trainingData.Animes[animeId].Type != MalAnimeType.Special)
                    {
                        filteredEntryList.Add(new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(animeId, trainingData.Users[userId].Entries[animeId]));
                    }
                }

                ReadOnlyMalListEntryDictionary filteredEntries = new ReadOnlyMalListEntryDictionary(filteredEntryList);

                MalUserListEntries filteredUser = new MalUserListEntries(filteredEntries, trainingData.Users[userId].AnimesEligibleForRecommendation,
                                                                         trainingData.Users[userId].MalUsername, trainingData.Users[userId].OkToRecommendPredicate);
                filteredUsers[userId] = filteredUser;
            }

            MalTrainingData filteredTrainingData = new MalTrainingData(filteredUsers, trainingData.Animes);

            return(filteredTrainingData);
        }
        public IEnumerable <RatingPredictionRecommendation> GetRecommendations(MalUserListEntries inputForUser, int numRecommendationsToTryToGet)
        {
            IBasicInputForUser basicInput = inputForUser.AsBasicInput(m_minEpisodesToCountIncomplete, m_useDropped,
                                                                      additionalOkToRecommendPredicate: (animeId) => m_userCountByAnime.ContainsKey(animeId) && m_userCountByAnime[animeId] >= m_minUsersToCountAnime
                                                                      );

            return(m_recommender.GetRecommendations(basicInput, numRecommendationsToTryToGet));
        }
예제 #3
0
        private static bool AnimeListContainsPrerequisitesFor(MalUserListEntries userAnimeList, IDictionary <int, IList <int> > prereqs,
                                                              int animeId)
        {
            IList <int> animePrereqs;

            if (!prereqs.TryGetValue(animeId, out animePrereqs))
            {
                // No prereqs
                return(true);
            }
            foreach (int prereqId in animePrereqs)
            {
                // For each prereq, user must have it on their list as completed
                if (!userAnimeList.Entries.ContainsKey(prereqId) || userAnimeList.Entries[prereqId].Status != CompletionStatus.Completed)
                {
                    return(false);
                }
            }
            return(true);
        }
예제 #4
0
 private static bool AnimeListContainsPrerequisitesFor(MalUserListEntries userAnimeList, IDictionary<int, IList<int>> prereqs,
     int animeId)
 {
     IList<int> animePrereqs;
     if (!prereqs.TryGetValue(animeId, out animePrereqs))
     {
         // No prereqs
         return true;
     }
     foreach (int prereqId in animePrereqs)
     {
         // For each prereq, user must have it on their list as completed
         if (!userAnimeList.Entries.ContainsKey(prereqId) || userAnimeList.Entries[prereqId].Status != CompletionStatus.Completed)
         {
             return false;
         }
     }
     return true;
 }
        public MalPositiveFeedbackRecResults GetRecommendations(MalUserListEntries inputForUser, int numRecommendationsToTryToGet)
        {
            MalPositiveFeedbackInput input = new MalPositiveFeedbackInput(inputForUser, targetFraction: m_targetFraction);

            return(m_underlyingRecSource.GetRecommendations(input, numRecommendationsToTryToGet));
        }
예제 #6
0
        public MalTrainingData LoadMalTrainingData()
        {
            // Load all anime, then all users, then all entries

            Dictionary<int, mal_user> dbUsers = new Dictionary<int, mal_user>();

            Logging.Log.Debug("Slurping anime from the database.");
            IEnumerable<mal_anime> dbAnimeSlurp = mal_anime.GetAll(m_conn, transaction: null);
            Logging.Log.Debug("Processing anime from the database.");
            Dictionary<int, MalAnime> animes = new Dictionary<int, MalAnime>(dbAnimeSlurp.Count());
            foreach (mal_anime dbAnime in dbAnimeSlurp)
            {
                MalAnime anime = new MalAnime(
                    malAnimeId: dbAnime.mal_anime_id,
                    type: (MalAnimeType)dbAnime.mal_anime_type_id,
                    title: dbAnime.title
                );
                animes[dbAnime.mal_anime_id] = anime;
            }
            Logging.Log.DebugFormat("Done processing {0} anime from the database.", animes.Count);

            Logging.Log.Debug("Slurping users from the database.");
            IEnumerable<mal_user> dbUserSlurp = mal_user.GetAll(m_conn, transaction: null);
            Logging.Log.Debug("Processing users from the database.");
            foreach (mal_user dbUser in dbUserSlurp)
            {
                dbUsers[dbUser.mal_user_id] = dbUser;
            }
            Logging.Log.DebugFormat("Done processing {0} users from the database.", dbUsers.Count);

            string allEntriesSlimSql = @"
            SELECT mal_user_id, mal_anime_id, rating, mal_list_entry_status_id, num_episodes_watched
            FROM mal_list_entry
            ";

            Logging.Log.Debug("Slurping list entries from the database.");
            // Do not buffer list entries
            IEnumerable<mal_list_entry_slim> dbEntrySlurp = m_conn.Query<mal_list_entry_slim>(allEntriesSlimSql, buffered: false, commandTimeout: 60);
            Logging.Log.Debug("Processing list entries from the database.");
            long entryCount = 0;

            Dictionary<int, List<ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId>> entriesByUser
                = new Dictionary<int, List<ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId>>();

            foreach (mal_list_entry_slim dbEntry in dbEntrySlurp)
            {
                entryCount++;
                mal_user dbUser;
                if (!dbUsers.TryGetValue(dbEntry.mal_user_id, out dbUser) || !animes.ContainsKey(dbEntry.mal_anime_id))
                {
                    // Entry for an anime or user that wasn't in the database...there must have been an update going on between the time we got users, anime, and list entries
                    continue;
                }
                List<ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId> animeList;
                if (!entriesByUser.TryGetValue(dbEntry.mal_user_id, out animeList))
                {
                    animeList = new List<ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId>();
                    entriesByUser[dbEntry.mal_user_id] = animeList;
                }

                animeList.Add(new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(
                    animeId: dbEntry.mal_anime_id,
                    entry: new MalListEntry(
                        rating: (byte?)dbEntry.rating,
                        status: (CompletionStatus)dbEntry.mal_list_entry_status_id,
                        numEpisodesWatched: dbEntry.num_episodes_watched
                    )
                ));
            }

            Dictionary<int, MalUserListEntries> users = new Dictionary<int, MalUserListEntries>(dbUserSlurp.Count());
            foreach (int userId in entriesByUser.Keys)
            {
                List<ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId> animeList = entriesByUser[userId];
                animeList.Capacity = animeList.Count;
                ReadOnlyMalListEntryDictionary listEntries = new ReadOnlyMalListEntryDictionary(animeList);
                users[userId] = new MalUserListEntries(listEntries, animes, dbUsers[userId].mal_name, okToRecommendPredicate: null);
            }

            Logging.Log.DebugFormat("Done processing {0} list entries.", entryCount);

            return new MalTrainingData(users, animes);
        }
 public IEnumerable <MostPopularRecommendation> GetRecommendations(MalUserListEntries inputForUser, int numRecommendationsToTryToGet)
 {
     return(m_recommender.GetRecommendations(inputForUser, numRecommendationsToTryToGet));
 }
예제 #8
0
 public MalAnimeRecsInput(MalUserListEntries animeList, double targetFraction)
 {
     AnimeList      = animeList;
     TargetFraction = targetFraction;
 }
예제 #9
0
 public MalAnimeRecsInput(MalUserListEntries animeList, decimal targetScore)
 {
     AnimeList   = animeList;
     TargetScore = targetScore;
 }
예제 #10
0
 public MalPositiveFeedbackInput(MalUserListEntries animeList, decimal targetScore)
 {
     AnimeList   = animeList;
     TargetScore = targetScore;
 }
예제 #11
0
 public MalPositiveFeedbackInput(MalUserListEntries animeList, double targetFraction)
 {
     AnimeList      = animeList;
     TargetFraction = targetFraction;
 }
 public MalPositiveFeedbackInput(MalUserListEntries animeList, decimal targetScore)
 {
     AnimeList = animeList;
     TargetScore = targetScore;
 }
 public MalPositiveFeedbackInput(MalUserListEntries animeList, double targetFraction)
 {
     AnimeList = animeList;
     TargetFraction = targetFraction;
 }
예제 #14
0
        public GetMalRecsResponse GetMalRecs(GetMalRecsRequest request)
        {
            request.AssertArgumentNotNull("Payload");
            request.RecSourceName.AssertArgumentNotNull("Payload.RecSourceName");
            request.AnimeList.AssertArgumentNotNull("Payload.AnimeList");
            request.AnimeList.Entries.AssertArgumentNotNull("Payload.AnimeList.Entries");

            if (request.TargetScore == null && request.TargetFraction == null)
            {
                Error error = new Error(ErrorCodes.InvalidArgument, "Payload.TargetScore or Payload.TargetFraction must be set.");
                throw new RecServiceErrorException(error);
            }

            string targetScoreString;
            if (request.TargetFraction != null)
            {
                targetScoreString = request.TargetFraction.Value.ToString("P2");
            }
            else
            {
                targetScoreString = request.TargetScore.Value.ToString();
            }

            Logging.Log.InfoFormat("Request for {0} MAL recs using rec source {1}. User has {2} anime list entries. Target score is {3}.",
                request.NumRecsDesired, request.RecSourceName, request.AnimeList.Entries.Count, targetScoreString);

            // Acquire read lock on rec sources
            bool enteredLock;
            const int lockTimeoutInMs = 3000;
            using (var trainingDataReadLock = m_trainingDataLock.ScopedReadLock(lockTimeoutInMs, out enteredLock))
            {
                // If we couldn't get a read lock within 3 seconds, a reload/retrain is probably going on
                if (!enteredLock)
                {
                    Error error = new Error(errorCode: ErrorCodes.Maintenance,
                        message: "The rec service is currently undergoing maintenance and cannot respond to rec requests.");
                    throw new RecServiceErrorException(error);
                }

                using (var recSourcesReadLock = m_recSourcesLock.ScopedReadLock())
                {
                    // Get rec source by name
                    if (!m_recSources.ContainsKey(request.RecSourceName))
                    {
                        Error error = new Error(errorCode: ErrorCodes.NoSuchRecSource,
                            message: string.Format("No rec source called \"{0}\" is loaded.", request.RecSourceName));
                        throw new RecServiceErrorException(error);
                    }
                    ITrainableJsonRecSource recSource = m_recSources[request.RecSourceName];

                    // Convert DTO anime list to RecEngine anime list

                    Dictionary<int, AnimeRecs.RecEngine.MAL.MalListEntry> entries = new Dictionary<int, RecEngine.MAL.MalListEntry>();
                    foreach (AnimeRecs.RecService.DTO.MalListEntry dtoEntry in request.AnimeList.Entries)
                    {
                        AnimeRecs.RecEngine.MAL.MalListEntry recEngineEntry = new RecEngine.MAL.MalListEntry(dtoEntry.Rating, dtoEntry.Status, dtoEntry.NumEpisodesWatched);
                        entries[dtoEntry.MalAnimeId] = recEngineEntry;
                    }
                    MalUserListEntries animeList = new MalUserListEntries(ratings: entries, animes: m_animes,
                        malUsername: null, prerequisites: m_prereqs);

                    Stopwatch timer = Stopwatch.StartNew();
                    GetMalRecsResponse response = recSource.GetRecommendations(animeList, request);
                    timer.Stop();

                    Logging.Log.InfoFormat("Got recommendations from rec source {0}. Took {1}.", request.RecSourceName, timer.Elapsed);
                    return response;
                }
            }
        }