private async Task <MalRecResults <IEnumerable <IRecommendation> > > GetMalRecommendationsAsync(Operation <GetMalRecsRequest> operation, TimeSpan timeout, CancellationToken cancellationToken) { GetMalRecsResponse response = await DoOperationWithResponseBodyAsync <GetMalRecsResponse>( operation, timeout, cancellationToken, descriptionForErrors : "getting recommendations").ConfigureAwait(false); // This should be set as if we were running against an in-process rec source. // So it should be an IEnumerable<AverageScoreRecommendation> if getting recs from an AverageScore rec source, etc. // MalRecResultsExtensions.cs contains extension methods for "casting" MalRecResults to a strongly-typed MalRecResults. IEnumerable <IRecommendation> results; Type responseType = response.GetType(); // check if m_responseToRecs implements IResponseToRecsConverter<responseType> Type converterInterfaceOpenType = typeof(IResponseToRecsConverter <>); Type converterInterfaceClosedType = converterInterfaceOpenType.MakeGenericType(responseType); if (converterInterfaceClosedType.IsInstanceOfType(m_responseToRecs)) { // Good, we have a handler. MethodInfo converterMethod = converterInterfaceClosedType.GetMethod(ResponseToRecsConverter.ConvertMethodName); object conversionResultObj = converterMethod.Invoke(m_responseToRecs, new object[] { response }); results = (IEnumerable <IRecommendation>)conversionResultObj; } else { // Fallback. This will throw an exception if the recommendation DTO type is registered in the DTO assembly // but no handler is registered in this assembly. GetMalRecsResponse <DTO.Recommendation> specificResponse = (GetMalRecsResponse <DTO.Recommendation>)response; List <IRecommendation> recommendations = new List <IRecommendation>(); foreach (DTO.Recommendation dtoRec in specificResponse.Recommendations) { recommendations.Add(new BasicRecommendation(dtoRec.MalAnimeId)); } results = recommendations; } Dictionary <int, RecEngine.MAL.MalAnime> animes = new Dictionary <int, RecEngine.MAL.MalAnime>(); foreach (DTO.MalAnime dtoAnime in response.Animes) { animes[dtoAnime.MalAnimeId] = new RecEngine.MAL.MalAnime(dtoAnime.MalAnimeId, dtoAnime.MalAnimeType, dtoAnime.Title); } MalRecResults <IEnumerable <IRecommendation> > resultsWithAnimes = new MalRecResults <IEnumerable <IRecommendation> >(results, animes, response.RecommendationType); return(resultsWithAnimes); }
protected override void SetSpecializedExtraResponseProperties( GetMalRecsResponse <DTO.AnimeRecsRecommendation, MalAnimeRecsExtraResponseData> response, MalAnimeRecsResults recResults) { response.Data = new MalAnimeRecsExtraResponseData( targetScoreUsed: recResults.TargetScoreUsed, recommenders: recResults.Recommenders.Select(recommender => new DTO.MalAnimeRecsRecommender( userId: recommender.UserId, username: UsernamesByUserId[recommender.UserId], recs: recommender.AllRecommendations.Select(recommendedAnime => new DTO.MalAnimeRecsRecommenderRecommendation( malAnimeId: recommendedAnime.MalAnimeId, judgment: recommender.RecsLiked.Contains(recommendedAnime) ? DTO.AnimeRecsRecommendationJudgment.Liked : recommender.RecsNotLiked.Contains(recommendedAnime) ? DTO.AnimeRecsRecommendationJudgment.NotLiked : recommender.RecsInconclusive.Contains(recommendedAnime) ? DTO.AnimeRecsRecommendationJudgment.Inconclusive : AnimeRecsRecommendationJudgment.NotInCommon, recommenderScore: recommendedAnime.RecommenderScore, averageScore: recommendedAnime.AverageScore )).ToList(), compatibility: recommender.Compatibility, compatibilityLowEndpoint: recommender.CompatibilityLowEndpoint, compatibilityHighEndpoint: recommender.CompatibilityHighEndpoint )).ToList() ); }
IEnumerable <RecEngine.IRecommendation> IResponseToRecsConverter <GetMalRecsResponse <RatingPredictionRecommendation> > .ConvertResponseToRecommendations(GetMalRecsResponse <RatingPredictionRecommendation> response) { List <RecEngine.RatingPredictionRecommendation> recommendations = new List <RecEngine.RatingPredictionRecommendation>(); foreach (DTO.RatingPredictionRecommendation dtoRec in response.Recommendations) { recommendations.Add(new RecEngine.RatingPredictionRecommendation( itemId: dtoRec.MalAnimeId, predictedRating: dtoRec.PredictedRating )); } return(recommendations); }
protected override void SetSpecializedExtraResponseProperties( GetMalRecsResponse <DTO.RatingPredictionRecommendation, MalPositiveFeedbackExtraResponseData> response, MalPositiveFeedbackRecResults recResults) { response.Data = new MalPositiveFeedbackExtraResponseData(targetScoreUsed: recResults.TargetScoreUsed); }
// Take in type derived from GetMalRecsResponse, return IEnumerable<IRecommendation> IEnumerable <RecEngine.IRecommendation> IResponseToRecsConverter <GetMalRecsResponse <MostPopularRecommendation> > .ConvertResponseToRecommendations(GetMalRecsResponse <MostPopularRecommendation> response) { List <RecEngine.MostPopularRecommendation> recommendations = new List <RecEngine.MostPopularRecommendation>(); foreach (DTO.MostPopularRecommendation dtoRec in response.Recommendations) { recommendations.Add(new AnimeRecs.RecEngine.MostPopularRecommendation( itemId: dtoRec.MalAnimeId, popularityRank: dtoRec.PopularityRank, numRatings: dtoRec.NumRatings )); } return(recommendations); }
// Take in type derived from GetMalRecsResponse, return IEnumerable<IRecommendation> IEnumerable <RecEngine.IRecommendation> IResponseToRecsConverter <GetMalRecsResponse <DTO.AnimeRecsRecommendation, DTO.MalAnimeRecsExtraResponseData> > .ConvertResponseToRecommendations(GetMalRecsResponse <DTO.AnimeRecsRecommendation, DTO.MalAnimeRecsExtraResponseData> response) { List <RecEngine.AnimeRecsRecommendation> recommendations = new List <RecEngine.AnimeRecsRecommendation>(); foreach (DTO.AnimeRecsRecommendation dtoRec in response.Recommendations) { recommendations.Add(new RecEngine.AnimeRecsRecommendation(dtoRec.RecommenderUserId, itemId: dtoRec.MalAnimeId)); } List <MalAnimeRecsRecommenderUser> recommenders = new List <MalAnimeRecsRecommenderUser>(); foreach (DTO.MalAnimeRecsRecommender dtoRecommender in response.Data.Recommenders) { HashSet <RecEngine.MAL.MalAnimeRecsRecommenderRecommendation> recsLiked = new HashSet <RecEngine.MAL.MalAnimeRecsRecommenderRecommendation>(); HashSet <RecEngine.MAL.MalAnimeRecsRecommenderRecommendation> recsNotLiked = new HashSet <RecEngine.MAL.MalAnimeRecsRecommenderRecommendation>(); HashSet <RecEngine.MAL.MalAnimeRecsRecommenderRecommendation> recsInconclusive = new HashSet <RecEngine.MAL.MalAnimeRecsRecommenderRecommendation>(); HashSet <RecEngine.MAL.MalAnimeRecsRecommenderRecommendation> recsNotInCommon = new HashSet <RecEngine.MAL.MalAnimeRecsRecommenderRecommendation>(); foreach (DTO.MalAnimeRecsRecommenderRecommendation rec in dtoRecommender.Recs) { switch (rec.Judgment) { case AnimeRecsRecommendationJudgment.Liked: recsLiked.Add(new RecEngine.MAL.MalAnimeRecsRecommenderRecommendation(rec.MalAnimeId, rec.RecommenderScore, rec.AverageScore)); break; case AnimeRecsRecommendationJudgment.NotLiked: recsNotLiked.Add(new RecEngine.MAL.MalAnimeRecsRecommenderRecommendation(rec.MalAnimeId, rec.RecommenderScore, rec.AverageScore)); break; case AnimeRecsRecommendationJudgment.Inconclusive: recsInconclusive.Add(new RecEngine.MAL.MalAnimeRecsRecommenderRecommendation(rec.MalAnimeId, rec.RecommenderScore, rec.AverageScore)); break; case AnimeRecsRecommendationJudgment.NotInCommon: recsNotInCommon.Add(new RecEngine.MAL.MalAnimeRecsRecommenderRecommendation(rec.MalAnimeId, rec.RecommenderScore, rec.AverageScore)); break; } } recommenders.Add(new MalAnimeRecsRecommenderUser( userId: dtoRecommender.UserId, username: dtoRecommender.Username, recsLiked: recsLiked, recsNotLiked: recsNotLiked, recsInconclusive: recsInconclusive, recsNotInCommon: recsNotInCommon, compatibility: dtoRecommender.Compatibility, compatibilityLowEndpoint: dtoRecommender.CompatibilityLowEndpoint, compatibilityHighEndpoint: dtoRecommender.CompatibilityHighEndpoint )); } return(new RecEngine.MAL.MalAnimeRecsResults(recommendations, recommenders, response.Data.TargetScoreUsed)); }
// Take in type derived from GetMalRecsResponse, return IEnumerable<IRecommendation> IEnumerable <RecEngine.IRecommendation> IResponseToRecsConverter <GetMalRecsResponse <AverageScoreRecommendation> > .ConvertResponseToRecommendations(GetMalRecsResponse <AverageScoreRecommendation> response) { List <RecEngine.AverageScoreRecommendation> recommendations = new List <RecEngine.AverageScoreRecommendation>(); foreach (DTO.AverageScoreRecommendation dtoRec in response.Recommendations) { recommendations.Add(new AnimeRecs.RecEngine.AverageScoreRecommendation(dtoRec.MalAnimeId, dtoRec.NumRatings, dtoRec.AverageScore)); } return(recommendations); }
public async Task <GetMalRecsResponse> GetMalRecsAsync(GetMalRecsRequest request, CancellationToken cancellationToken) { request.AssertArgumentNotNull("request"); request.RecSourceName.AssertArgumentNotNull("request.RecSourceName"); request.AnimeList.AssertArgumentNotNull("request.AnimeList"); request.AnimeList.Entries.AssertArgumentNotNull("request.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 const int lockTimeoutInSeconds = 3; using (CancellationTokenSource lockTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(lockTimeoutInSeconds))) using (CancellationTokenSource lockCancel = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, lockTimeout.Token)) { bool gotLock = false; try { using (var recSourcesLock = await m_recSourcesLockAsync.EnterReadLockAsync(lockCancel.Token).ConfigureAwait(false)) { gotLock = true; // 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, cancellationToken); timer.Stop(); Logging.Log.InfoFormat("Got recommendations from rec source {0}. Took {1}.", request.RecSourceName, timer.Elapsed); return(response); } } catch (OperationCanceledException) { // If we couldn't get a read lock within 3 seconds, a reload/retrain is probably going on if (!gotLock) { 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); } else { throw; } } } }