public DTO.GetMalRecsResponse GetRecommendations(MalUserListEntries animeList, GetMalRecsRequest recRequest, CancellationToken cancellationToken) { // Ignore the cancellation token - we're only cancelling on service shut down, and getting recommendations should be quick. // If getting recommendations takes longer than the final connection drain time limit, something is wrong. TInput recSourceInput = GetRecSourceInputFromRequest(animeList, recRequest); TRecommendationResults recResults = UnderlyingRecSource.GetRecommendations(recSourceInput, recRequest.NumRecsDesired); List <TDtoRec> dtoRecs = new List <TDtoRec>(); Dictionary <int, DTO.MalAnime> animes = new Dictionary <int, DTO.MalAnime>(); foreach (TRecommendation rec in recResults) { TDtoRec dtoRec = new TDtoRec() { MalAnimeId = rec.ItemId, }; animes[rec.ItemId] = new DTO.MalAnime(rec.ItemId, Animes[rec.ItemId].Title, Animes[rec.ItemId].Type); SetSpecializedRecommendationProperties(dtoRec, rec); dtoRecs.Add(dtoRec); } TResponse response = new TResponse(); response.RecommendationType = RecommendationType; response.Recommendations = dtoRecs; SetSpecializedExtraResponseProperties(response, recResults); HashSet <int> extraAnimesToReturn = GetExtraAnimesToReturn(recResults); foreach (int extraAnimeId in extraAnimesToReturn) { if (!animes.ContainsKey(extraAnimeId)) { animes[extraAnimeId] = new DTO.MalAnime(extraAnimeId, Animes[extraAnimeId].Title, Animes[extraAnimeId].Type); } } response.Animes = animes.Values.ToList(); return(response); }
protected override MalUserListEntries GetRecSourceInputFromRequest(MalUserListEntries animeList, GetMalRecsRequest recRequest) { return(animeList); }
/// <summary> /// Converts the user's anime list into the input used by the rec source. /// </summary> /// <param name="animeList"></param> /// <param name="recRequest"></param> /// <param name="caster"></param> /// <returns></returns> protected abstract TInput GetRecSourceInputFromRequest(MalUserListEntries animeList, GetMalRecsRequest recRequest);
protected override MalPositiveFeedbackInput GetRecSourceInputFromRequest(MalUserListEntries animeList, GetMalRecsRequest recRequest) { if (recRequest.TargetScore != null) { return(new MalPositiveFeedbackInput(animeList, recRequest.TargetScore.Value)); } else { return(new MalPositiveFeedbackInput(animeList, recRequest.TargetFraction.Value)); } }
public Task <MalRecResults <IEnumerable <IRecommendation> > > GetMalRecommendationsWithFractionTargetAsync( IDictionary <int, RecEngine.MAL.MalListEntry> animeList, string recSourceName, int numRecsDesired, decimal targetFraction, TimeSpan timeout, CancellationToken cancellationToken) { List <DTO.MalListEntry> dtoAnimeList = CreateDtoAnimeList(animeList); Operation <GetMalRecsRequest> operation = new Operation <GetMalRecsRequest>(OperationTypes.GetMalRecs, GetMalRecsRequest.CreateWithTargetFraction(recSourceName, numRecsDesired, targetFraction, new MalListForUser(dtoAnimeList))); return(GetMalRecommendationsAsync(operation, timeout, cancellationToken)); }
protected override MalAnimeRecsInput GetRecSourceInputFromRequest(MalUserListEntries animeList, GetMalRecsRequest recRequest) { if (recRequest.TargetScore != null) { return(new MalAnimeRecsInput(animeList, targetScore: recRequest.TargetScore.Value)); } else { return(new MalAnimeRecsInput(animeList, targetFraction: (double)recRequest.TargetFraction.Value)); } }
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; } } } }