public void TestIndexing()
        {
            List<ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId> entries = new List<ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId>()
            {
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(3, new MalListEntry(1, CompletionStatus.Completed, 3)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(1, new MalListEntry(2, CompletionStatus.Completed, 1)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(9, new MalListEntry(3, CompletionStatus.Completed, 9)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(25, new MalListEntry(4, CompletionStatus.Completed, 25)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(2, new MalListEntry(5, CompletionStatus.Completed, 2)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(6, new MalListEntry(6, CompletionStatus.Completed, 6))
            };

            ReadOnlyMalListEntryDictionary dict = new ReadOnlyMalListEntryDictionary(entries);
            Assert.That(dict[3].NumEpisodesWatched, Is.EqualTo(3));
            Assert.That(dict[1].NumEpisodesWatched, Is.EqualTo(1));
            Assert.That(dict[9].NumEpisodesWatched, Is.EqualTo(9));
            Assert.That(dict[25].NumEpisodesWatched, Is.EqualTo(25));
            Assert.That(dict[2].NumEpisodesWatched, Is.EqualTo(2));
            Assert.That(dict[6].NumEpisodesWatched, Is.EqualTo(6));
        }
        public void TestIndexing()
        {
            List <ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId> entries = new List <ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId>()
            {
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(3, new MalListEntry(1, CompletionStatus.Completed, 3)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(1, new MalListEntry(2, CompletionStatus.Completed, 1)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(9, new MalListEntry(3, CompletionStatus.Completed, 9)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(25, new MalListEntry(4, CompletionStatus.Completed, 25)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(2, new MalListEntry(5, CompletionStatus.Completed, 2)),
                new ReadOnlyMalListEntryDictionary.ListEntryAndAnimeId(6, new MalListEntry(6, CompletionStatus.Completed, 6))
            };

            ReadOnlyMalListEntryDictionary dict = new ReadOnlyMalListEntryDictionary(entries);

            Assert.Equal(3, dict[3].NumEpisodesWatched);
            Assert.Equal(1, dict[1].NumEpisodesWatched);
            Assert.Equal(9, dict[9].NumEpisodesWatched);
            Assert.Equal(25, dict[25].NumEpisodesWatched);
            Assert.Equal(2, dict[2].NumEpisodesWatched);
            Assert.Equal(6, dict[6].NumEpisodesWatched);
        }
        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);
        }
Beispiel #4
0
        public async Task <MalTrainingData> LoadMalTrainingDataAsync(CancellationToken cancellationToken)
        {
            // Load all anime, users, and entries in parallel, then combine them into training data.
            try
            {
                Dictionary <int, MalAnime>  animes;
                Dictionary <int, mal_user>  dbUsers;
                IList <mal_list_entry_slim> dbEntrySlurp;

                using (CancellationTokenSource faultCanceler = new CancellationTokenSource())
                    using (CancellationTokenSource faultOrUserCancel = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, faultCanceler.Token))
                    {
                        Task <Dictionary <int, MalAnime> > animeTask = AsyncUtils.EnsureExceptionsWrapped(
                            () => SlurpAnimeAsync(faultOrUserCancel.Token));
                        CancellableTask cancellableAnimeTask = new CancellableTask(animeTask, faultCanceler);

                        Task <Dictionary <int, mal_user> > userTask = AsyncUtils.EnsureExceptionsWrapped(
                            () => SlurpUsersAsync(faultOrUserCancel.Token));
                        CancellableTask cancellableUserTask = new CancellableTask(userTask, faultCanceler);

                        Task <IList <mal_list_entry_slim> > entryTask = AsyncUtils.EnsureExceptionsWrapped(
                            () => SlurpEntriesAsync(faultOrUserCancel.Token));
                        CancellableTask cancellableEntryTask = new CancellableTask(entryTask, faultCanceler);

                        await AsyncUtils.WhenAllCancelOnFirstExceptionDontWaitForCancellations(cancellableEntryTask, cancellableAnimeTask, cancellableUserTask).ConfigureAwait(false);

                        animes       = animeTask.Result;
                        dbUsers      = userTask.Result;
                        dbEntrySlurp = entryTask.Result;
                    }

                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>(dbUsers.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));
            }
            catch (Exception ex) when(!(ex is OperationCanceledException))
            {
                throw new Exception(string.Format("Error loading MAL training data: {0}", ex.Message), ex);
            }
        }