예제 #1
0
 public void PrintResults(MalRecResults<IEnumerable<IRecommendation>> results, IDictionary<int, MalListEntry> animeList, decimal targetScore)
 {
     if (RegisteredPrinters.ContainsKey(results.RecommendationType))
     {
         RegisteredPrinters[results.RecommendationType](results, animeList, targetScore);
     }
     else
     {
         PrintFallbackResults(results, animeList, targetScore);
     }
 }
예제 #2
0
 public void PrintResults(MalRecResults <IEnumerable <IRecommendation> > results, IDictionary <int, MalListEntry> animeList, decimal targetScore)
 {
     if (RegisteredPrinters.ContainsKey(results.RecommendationType))
     {
         RegisteredPrinters[results.RecommendationType](results, animeList, targetScore);
     }
     else
     {
         PrintFallbackResults(results, animeList, targetScore);
     }
 }
예제 #3
0
        public async Task <IActionResult> GetRecs([FromBody] AnimeRecsInputJson input,
                                                  [FromServices] IOptionsSnapshot <Config.RecommendationsConfig> recConfig,
                                                  [FromServices] IMyAnimeListApiFactory malApiFactory, [FromServices] IAnimeRecsClientFactory recClientFactory,
                                                  [FromServices] IAnimeRecsDbConnectionFactory dbConnFactory, [FromServices] IRazorViewEngine viewEngine,
                                                  [FromServices] ITempDataProvider tempProvider)
        {
            if (!ModelState.IsValid)
            {
                AjaxError error = new AjaxError(ModelState);
                _logger.LogDebug("Invalid input received for GetRecs: {0}", error.Message);
                return(BadRequest(error));
            }

            if (input.RecSourceName == null)
            {
                input.RecSourceName = recConfig.Value.DefaultRecSource;
            }

            try
            {
                MalUserLookupResults userLookup = await GetUserLookupAsync(input, malApiFactory).ConfigureAwait(false);

                Dictionary <int, MalListEntry> animeList = new Dictionary <int, MalListEntry>();
                foreach (MyAnimeListEntry listEntry in userLookup.AnimeList)
                {
                    animeList[listEntry.AnimeInfo.AnimeId] = new AnimeRecs.RecEngine.MAL.MalListEntry((byte?)listEntry.Score, listEntry.Status, (short)listEntry.NumEpisodesWatched);
                }

                Dictionary <int, MalListEntry> animeWithheld = WithholdAnime(input, animeList);

                MalRecResults <IEnumerable <IRecommendation> > recResults = await GetRecommendationsAsync(input, recConfig.Value, animeList, animeWithheld, recClientFactory).ConfigureAwait(false);

                GetRecsViewModel viewModel = new GetRecsViewModel(
                    results: recResults,
                    userId: userLookup.UserId,
                    userName: userLookup.CanonicalUserName,
                    userLookup: userLookup,
                    userAnimeList: animeList,
                    maximumRecommendationsToReturn: recConfig.Value.MaximumRecommendationsToReturn,
                    maximumRecommendersToReturn: recConfig.Value.MaximumRecommendersToReturn,
                    animeWithheld: animeWithheld,
                    dbConnectionFactory: dbConnFactory
                    );

                RecResultsAsHtmlJson resultsJson = await GetResultHtmlAsync(viewModel, input, viewEngine, tempProvider).ConfigureAwait(false);

                return(Ok(resultsJson));
            }
            catch (ShortCircuitException ex)
            {
                return(ex.Result);
            }
        }
예제 #4
0
        private static void PrintRecs(MalRecResults <IEnumerable <RecEngine.IRecommendation> > recs, IDictionary <int, RecEngine.MAL.MalListEntry> animeList, decimal targetScore)
        {
            if (!recs.Results.Any())
            {
                Console.WriteLine("No recommendations.");
                return;
            }

            ResultsPrinter resultsPrinter = new ResultsPrinter();

            resultsPrinter.PrintResults(recs, animeList, targetScore);
        }
예제 #5
0
 public GetRecsViewModel(MalRecResults<IEnumerable<IRecommendation>> results, int userId, string userName,
     MalUserLookupResults userLookup, IDictionary<int, MalListEntry> userAnimeList,
     int maximumRecommendationsToReturn, int maximumRecommendersToReturn, IDictionary<int, MalListEntry> animeWithheld, IAnimeRecsDbConnectionFactory dbConnectionFactory)
 {
     Results = results;
     UserId = userId;
     UserName = userName;
     UserLookup = userLookup;
     UserAnimeList = userAnimeList;
     MaximumRecommendationsToReturn = maximumRecommendationsToReturn;
     MaximumRecommendersToReturn = maximumRecommendersToReturn;
     DbConnectionFactory = dbConnectionFactory;
     StreamsByAnime = new Dictionary<int, ICollection<streaming_service_anime_map>>();
     AnimeWithheld = animeWithheld;
 }
 private void PrintRatingPredictionResults(MalRecResults<IEnumerable<IRecommendation>> basicResults, IDictionary<int, MalListEntry> animeList, decimal targetScore)
 {
     MalRecResults<IEnumerable<RatingPredictionRecommendation>> results = basicResults.CastRecs<IEnumerable<RatingPredictionRecommendation>>();
     int recNumber = 1;
     foreach (var recIt in results.Results.AsSmartEnumerable())
     {
         RatingPredictionRecommendation rec = recIt.Value;
         if (recIt.IsFirst)
         {
             Console.WriteLine("     {0,-60} {1}", "Anime", "Prediction");
         }
         Console.WriteLine("{0,3}. {1,-60} {2:F3}", recNumber, results.AnimeInfo[rec.ItemId].Title, rec.PredictedRating);
         recNumber++;
     }
 }
        private void PrintRatingPredictionResults(MalRecResults <IEnumerable <IRecommendation> > basicResults, IDictionary <int, MalListEntry> animeList, decimal targetScore)
        {
            MalRecResults <IEnumerable <RatingPredictionRecommendation> > results = basicResults.CastRecs <IEnumerable <RatingPredictionRecommendation> >();
            int recNumber = 1;

            foreach (RatingPredictionRecommendation rec in results.Results)
            {
                if (recNumber == 1)
                {
                    Console.WriteLine("     {0,-60} {1}", "Anime", "Prediction");
                }
                Console.WriteLine("{0,3}. {1,-60} {2:F3}", recNumber, results.AnimeInfo[rec.ItemId].Title, rec.PredictedRating);
                recNumber++;
            }
        }
        private void PrintFallbackResults(MalRecResults <IEnumerable <IRecommendation> > results, IDictionary <int, MalListEntry> animeList, decimal targetScore)
        {
            int recNumber = 1;

            foreach (IRecommendation rec in results.Results)
            {
                if (recNumber == 1)
                {
                    Console.WriteLine("     {0,-65}", "Anime");
                }

                Console.WriteLine("{0,3}. {1.-65}", recNumber, results.AnimeInfo[rec.ItemId].Title);
                recNumber++;
            }
        }
        private void PrintFallbackResults(MalRecResults<IEnumerable<IRecommendation>> results, IDictionary<int, MalListEntry> animeList, decimal targetScore)
        {
            int recNumber = 1;
            foreach (var recIt in results.Results.AsSmartEnumerable())
            {
                IRecommendation rec = recIt.Value;
                if (recIt.IsFirst)
                {
                    Console.WriteLine("     {0,-65}", "Anime");
                }

                Console.WriteLine("{0,3}. {1.-65}", recNumber, results.AnimeInfo[rec.ItemId].Title);
                recNumber++;
            }
        }
예제 #10
0
 public GetRecsViewModel(MalRecResults <IEnumerable <IRecommendation> > results, int userId, string userName,
                         MalUserLookupResults userLookup, IDictionary <int, MalListEntry> userAnimeList,
                         int maximumRecommendationsToReturn, int maximumRecommendersToReturn, IDictionary <int, MalListEntry> animeWithheld, IAnimeRecsDbConnectionFactory dbConnectionFactory)
 {
     Results       = results;
     UserId        = userId;
     UserName      = userName;
     UserLookup    = userLookup;
     UserAnimeList = userAnimeList;
     MaximumRecommendationsToReturn = maximumRecommendationsToReturn;
     MaximumRecommendersToReturn    = maximumRecommendersToReturn;
     DbConnectionFactory            = dbConnectionFactory;
     StreamsByAnime = new Dictionary <int, ICollection <streaming_service_anime_map> >();
     AnimeWithheld  = animeWithheld;
 }
예제 #11
0
        private void PrintAverageScoreResults(MalRecResults <IEnumerable <IRecommendation> > basicResults, IDictionary <int, MalListEntry> animeList, decimal targetScore)
        {
            MalRecResults <IEnumerable <AverageScoreRecommendation> > results = basicResults.CastRecs <IEnumerable <AverageScoreRecommendation> >();
            int recNumber = 1;

            foreach (AverageScoreRecommendation rec in results.Results)
            {
                if (recNumber == 1)
                {
                    Console.WriteLine("     {0,-52} {1,-6} {2}", "Anime", "Avg", "# ratings");
                }

                Console.WriteLine("{0,3}. {1,-52} {2,-6:f2} {3}", recNumber, results.AnimeInfo[rec.ItemId].Title, rec.AverageScore, rec.NumRatings);
                recNumber++;
            }
        }
        private void PrintMostPopularResults(MalRecResults<IEnumerable<IRecommendation>> basicResults, IDictionary<int, MalListEntry> animeList, decimal targetScore)
        {
            MalRecResults<IEnumerable<MostPopularRecommendation>> results = basicResults.CastRecs<IEnumerable<MostPopularRecommendation>>();
            int recNumber = 1;
            foreach (var recIt in results.Results.AsSmartEnumerable())
            {
                MostPopularRecommendation rec = recIt.Value;
                if (recIt.IsFirst)
                {
                    Console.WriteLine("     {0,-52} {1,4} {2}", "Anime", "Rank", "# ratings");
                }

                Console.WriteLine("{0,3}. {1,-52} {2,4} {3}", recNumber, results.AnimeInfo[rec.ItemId].Title, rec.PopularityRank, rec.NumRatings);
                recNumber++;
            }
        }
        private void PrintAnimeRecsResults(MalRecResults<IEnumerable<IRecommendation>> basicResults, IDictionary<int, MalListEntry> animeList, decimal targetScore)
        {
            MalRecResults<MalAnimeRecsResults> results = basicResults.CastRecs<MalAnimeRecsResults>();

            int numRecommendersPrinted = 0;

            foreach (MalAnimeRecsRecommenderUser recommender in results.Results.Recommenders)
            {
                if (numRecommendersPrinted > 10)
                {
                    break;
                }

                string recsLikedString;

                if (recommender.NumRecsWithJudgment > 0)
                {
                    recsLikedString = string.Format("{0:P2}", (double)recommender.RecsLiked.Count / recommender.NumRecsWithJudgment);
                }
                else
                {
                    recsLikedString = string.Format("{0:P2}", 0);
                }

                Console.WriteLine("{0}'s recommendations ({1}/{2} {3} recs liked, {4:P2} - {5:P2} estimated compatibility",
                    recommender.Username, recommender.RecsLiked.Count, recommender.NumRecsWithJudgment, recsLikedString,
                    recommender.CompatibilityLowEndpoint ?? 0, recommender.CompatibilityHighEndpoint ?? 0);

                Console.WriteLine("{0,-52} {1,-5} {2,-4} {3,-5} {4,-4} {5}", "Anime", "State", "Like", "Their", "Your", "Avg");

                foreach (MalAnimeRecsRecommenderRecommendation recommendation in recommender.AllRecommendations.OrderBy(
                    rec => !recommender.RecsLiked.Contains(rec) && !recommender.RecsNotLiked.Contains(rec) ? 0 :
                        recommender.RecsLiked.Contains(rec) ? 1 :
                        2
                    )
                    .ThenByDescending(rec => rec.RecommenderScore)
                    .ThenByDescending(rec => rec.AverageScore))
                {
                    string status;
                    decimal? yourRating = null;
                    if (!animeList.ContainsKey(recommendation.MalAnimeId))
                    {
                        status = "-"; // not watched, not in list
                    }
                    else
                    {
                        if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.Completed)
                            status = "comp";
                        else if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.Dropped)
                            status = "drop";
                        else if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.OnHold)
                            status = "hold";
                        else if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.PlanToWatch)
                            status = "plan";
                        else if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.Watching)
                            status = "watch";
                        else
                            status = "?";

                        yourRating = animeList[recommendation.MalAnimeId].Rating;
                    }

                    string yourRatingString;
                    if (yourRating != null)
                    {
                        yourRatingString = yourRating.Value.ToString(CultureInfo.CurrentCulture);
                    }
                    else
                    {
                        yourRatingString = "-";
                    }

                    string likedString;
                    if (recommender.RecsLiked.Contains(recommendation))
                        likedString = "+";
                    else if (recommender.RecsNotLiked.Contains(recommendation))
                        likedString = "-";
                    else
                        likedString = "?";

                    Console.WriteLine("{0,-52} {1,-5} {2,-4} {3,-5:F0} {4,-4:F0} {5:F2}",
                        results.AnimeInfo[recommendation.MalAnimeId].Title, status, likedString, recommendation.RecommenderScore,
                        yourRatingString, recommendation.AverageScore);
                }

                Console.WriteLine();
                Console.WriteLine();

                numRecommendersPrinted++;
            }
        }
예제 #14
0
        private static void PrintRecs(MalRecResults<IEnumerable<RecEngine.IRecommendation>> recs, IDictionary<int, RecEngine.MAL.MalListEntry> animeList, decimal targetScore)
        {
            if (!recs.Results.Any())
            {
                Console.WriteLine("No recommendations.");
                return;
            }

            ResultsPrinter resultsPrinter = new ResultsPrinter();
            resultsPrinter.PrintResults(recs, animeList, targetScore);
        }
예제 #15
0
        static void Main(string[] args)
        {
            CommandLineArgs commandLine = new CommandLineArgs(args);

            if (commandLine.ShowHelp)
            {
                commandLine.DisplayHelp(Console.Out);
                return;
            }

            Config config = Config.LoadFromFile(commandLine.ConfigFile);

            using (AnimeRecsClient client = new AnimeRecsClient(commandLine.PortNumber))
            {
                if (commandLine.Operation.Equals("raw", StringComparison.OrdinalIgnoreCase))
                {
                    byte[] requestJsonBytes          = Encoding.UTF8.GetBytes(commandLine.RawJson);
                    int    requestLength             = requestJsonBytes.Length;
                    int    requestLengthNetworkOrder = IPAddress.HostToNetworkOrder(requestLength);
                    byte[] requestLengthBytes        = BitConverter.GetBytes(requestLengthNetworkOrder);

                    byte[] requestBytes = new byte[requestLengthBytes.Length + requestJsonBytes.Length];
                    requestLengthBytes.CopyTo(requestBytes, index: 0);
                    requestJsonBytes.CopyTo(requestBytes, index: requestLengthBytes.Length);

                    using (Socket rawClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                    {
                        TimeSpan sendTimeout = TimeSpan.FromSeconds(5);
                        rawClientSocket.SendAllAsync(requestBytes, sendTimeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                        rawClientSocket.Shutdown(SocketShutdown.Send);

                        byte[]  responseLengthBuffer       = rawClientSocket.ReceiveAllAsync(numBytesToReceive: 4, receiveAllTimeout: TimeSpan.FromMinutes(3), cancellationToken: CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                        int     responseLengthNetworkOrder = BitConverter.ToInt32(responseLengthBuffer, 0);
                        int     responseLength             = IPAddress.NetworkToHostOrder(responseLengthNetworkOrder);
                        byte[]  responseJsonBytes          = rawClientSocket.ReceiveAllAsync(responseLength, receiveAllTimeout: TimeSpan.FromSeconds(5), cancellationToken: CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                        string  responseJsonString         = Encoding.UTF8.GetString(responseJsonBytes);
                        dynamic responseJson   = JsonConvert.DeserializeObject <dynamic>(responseJsonString);
                        string  prettyResponse = JsonConvert.SerializeObject(responseJson, Formatting.Indented);
                        Console.WriteLine(prettyResponse);
                    }
                }
                else if (commandLine.Operation.Equals(OperationTypes.Ping, StringComparison.OrdinalIgnoreCase))
                {
                    TimeSpan  timeout      = TimeSpan.FromSeconds(3);
                    Stopwatch timer        = Stopwatch.StartNew();
                    string    pingResponse = client.PingAsync(commandLine.PingMessage, timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                    timer.Stop();
                    Console.WriteLine($"The service replied: {pingResponse} (took {timer.Elapsed})");
                }
                else if (commandLine.Operation.Equals(OperationTypes.ReloadTrainingData, StringComparison.OrdinalIgnoreCase))
                {
                    TimeSpan timeout = TimeSpan.FromMinutes(3);
                    client.ReloadTrainingDataAsync(commandLine.ReloadMode, commandLine.Finalize, timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                    Console.WriteLine("Training data reloaded.");
                }
                else if (commandLine.Operation.Equals(OperationTypes.FinalizeRecSources, StringComparison.OrdinalIgnoreCase))
                {
                    TimeSpan timeout = TimeSpan.FromSeconds(5);
                    client.FinalizeRecSourcesAsync(timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();;
                    Console.WriteLine("Rec sources finalized.");
                }
                else if (commandLine.Operation.Equals(OperationTypes.LoadRecSource, StringComparison.OrdinalIgnoreCase))
                {
                    if (commandLine.RecSourceType.Equals(RecSourceTypes.AverageScore, StringComparison.OrdinalIgnoreCase))
                    {
                        TimeSpan timeout = TimeSpan.FromSeconds(30);
                        client.LoadRecSourceAsync(commandLine.RecSourceName, commandLine.ReplaceExistingRecSource,
                                                  new AverageScoreRecSourceParams(
                                                      minEpisodesToCountIncomplete: commandLine.MinEpisodesToCountIncomplete,
                                                      minUsersToCountAnime: commandLine.MinUsersToCountAnime,
                                                      useDropped: commandLine.UseDropped
                                                      ),
                                                  timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                    }
                    else if (commandLine.RecSourceType.Equals(RecSourceTypes.MostPopular, StringComparison.OrdinalIgnoreCase))
                    {
                        TimeSpan timeout = TimeSpan.FromSeconds(30);
                        client.LoadRecSourceAsync(commandLine.RecSourceName, commandLine.ReplaceExistingRecSource,
                                                  new MostPopularRecSourceParams(
                                                      minEpisodesToCountIncomplete: commandLine.MinEpisodesToCountIncomplete,
                                                      useDropped: commandLine.UseDropped
                                                      ), timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                    }
                    else if (commandLine.RecSourceType.Equals(RecSourceTypes.AnimeRecs, StringComparison.OrdinalIgnoreCase))
                    {
                        TimeSpan timeout = TimeSpan.FromSeconds(60);
                        client.LoadRecSourceAsync(commandLine.RecSourceName, commandLine.ReplaceExistingRecSource,
                                                  new AnimeRecsRecSourceParams(
                                                      numRecommendersToUse: commandLine.NumRecommendersToUse,
                                                      fractionConsideredRecommended: commandLine.FractionRecommended,
                                                      minEpisodesToClassifyIncomplete: commandLine.MinEpisodesToCountIncomplete
                                                      ), timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                    }
                    else if (commandLine.RecSourceType.Equals(RecSourceTypes.BiasedMatrixFactorization, StringComparison.OrdinalIgnoreCase))
                    {
                        TimeSpan timeout = TimeSpan.FromMinutes(3);
                        client.LoadRecSourceAsync(commandLine.RecSourceName, commandLine.ReplaceExistingRecSource,
                                                  commandLine.BiasedMatrixFactorizationParams, timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                    }
                    else
                    {
                        throw new Exception("Oops! Missed a rec source type!");
                    }

                    Console.WriteLine("Load complete.");
                }
                else if (commandLine.Operation.Equals(OperationTypes.UnloadRecSource, StringComparison.OrdinalIgnoreCase))
                {
                    TimeSpan timeout = TimeSpan.FromSeconds(10);
                    client.UnloadRecSourceAsync(commandLine.RecSourceName, timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                    Console.WriteLine("Unload complete.");
                }
                else if (commandLine.Operation.Equals(OperationTypes.GetRecSourceType, StringComparison.OrdinalIgnoreCase))
                {
                    TimeSpan timeout       = TimeSpan.FromSeconds(3);
                    string   recSourceType = client.GetRecSourceTypeAsync(commandLine.RecSourceName, timeout, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
                    Console.WriteLine("Type of rec source {0} is {1}.", commandLine.RecSourceName, recSourceType);
                }
                else if (commandLine.Operation.Equals(OperationTypes.GetMalRecs, StringComparison.OrdinalIgnoreCase))
                {
                    MalUserLookupResults lookup;

                    using (IMyAnimeListApi malApi = GetMalApi(config))
                    {
                        lookup = malApi.GetAnimeListForUser(commandLine.MalUsername);
                    }

                    Dictionary <int, RecEngine.MAL.MalListEntry> animeListEntries = new Dictionary <int, RecEngine.MAL.MalListEntry>();
                    foreach (MyAnimeListEntry entry in lookup.AnimeList)
                    {
                        animeListEntries[entry.AnimeInfo.AnimeId] = new RecEngine.MAL.MalListEntry((byte?)entry.Score, entry.Status, (short)entry.NumEpisodesWatched);
                    }

                    TimeSpan timeout = TimeSpan.FromSeconds(10);
                    MalRecResults <IEnumerable <RecEngine.IRecommendation> > recs = client.GetMalRecommendationsAsync(
                        animeList: animeListEntries,
                        recSourceName: commandLine.RecSourceName,
                        numRecsDesired: commandLine.NumRecs,
                        targetScore: commandLine.TargetScore,
                        timeout: timeout,
                        cancellationToken: CancellationToken.None
                        )
                                                                                    .ConfigureAwait(false).GetAwaiter().GetResult();

                    PrintRecs(recs, animeListEntries, commandLine.TargetScore);
                }
                else
                {
                    throw new Exception(string.Format("Oops, missed an operation: {0}", commandLine.Operation));
                }
            }
        }
        private void PrintAnimeRecsResults(MalRecResults <IEnumerable <IRecommendation> > basicResults, IDictionary <int, MalListEntry> animeList, decimal targetScore)
        {
            MalRecResults <MalAnimeRecsResults> results = basicResults.CastRecs <MalAnimeRecsResults>();

            int numRecommendersPrinted = 0;

            foreach (MalAnimeRecsRecommenderUser recommender in results.Results.Recommenders)
            {
                if (numRecommendersPrinted > 10)
                {
                    break;
                }

                string recsLikedString;

                if (recommender.NumRecsWithJudgment > 0)
                {
                    recsLikedString = string.Format("{0:P2}", (double)recommender.RecsLiked.Count / recommender.NumRecsWithJudgment);
                }
                else
                {
                    recsLikedString = string.Format("{0:P2}", 0);
                }

                Console.WriteLine("{0}'s recommendations ({1}/{2} {3} recs liked, {4:P2} - {5:P2} estimated compatibility",
                                  recommender.Username, recommender.RecsLiked.Count, recommender.NumRecsWithJudgment, recsLikedString,
                                  recommender.CompatibilityLowEndpoint ?? 0, recommender.CompatibilityHighEndpoint ?? 0);

                Console.WriteLine("{0,-52} {1,-5} {2,-4} {3,-5} {4,-4} {5}", "Anime", "State", "Like", "Their", "Your", "Avg");

                foreach (MalAnimeRecsRecommenderRecommendation recommendation in recommender.AllRecommendations.OrderBy(
                             rec => !recommender.RecsLiked.Contains(rec) && !recommender.RecsNotLiked.Contains(rec) ? 0 :
                             recommender.RecsLiked.Contains(rec) ? 1 :
                             2
                             )
                         .ThenByDescending(rec => rec.RecommenderScore)
                         .ThenByDescending(rec => rec.AverageScore))
                {
                    string  status;
                    decimal?yourRating = null;
                    if (!animeList.ContainsKey(recommendation.MalAnimeId))
                    {
                        status = "-"; // not watched, not in list
                    }
                    else
                    {
                        if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.Completed)
                        {
                            status = "comp";
                        }
                        else if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.Dropped)
                        {
                            status = "drop";
                        }
                        else if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.OnHold)
                        {
                            status = "hold";
                        }
                        else if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.PlanToWatch)
                        {
                            status = "plan";
                        }
                        else if (animeList[recommendation.MalAnimeId].Status == CompletionStatus.Watching)
                        {
                            status = "watch";
                        }
                        else
                        {
                            status = "?";
                        }

                        yourRating = animeList[recommendation.MalAnimeId].Rating;
                    }

                    string yourRatingString;
                    if (yourRating != null)
                    {
                        yourRatingString = yourRating.Value.ToString(CultureInfo.CurrentCulture);
                    }
                    else
                    {
                        yourRatingString = "-";
                    }

                    string likedString;
                    if (recommender.RecsLiked.Contains(recommendation))
                    {
                        likedString = "+";
                    }
                    else if (recommender.RecsNotLiked.Contains(recommendation))
                    {
                        likedString = "-";
                    }
                    else
                    {
                        likedString = "?";
                    }

                    Console.WriteLine("{0,-52} {1,-5} {2,-4} {3,-5:F0} {4,-4:F0} {5:F2}",
                                      results.AnimeInfo[recommendation.MalAnimeId].Title, status, likedString, recommendation.RecommenderScore,
                                      yourRatingString, recommendation.AverageScore);
                }

                Console.WriteLine();
                Console.WriteLine();

                numRecommendersPrinted++;
            }
        }