public async Task <ResultModel> RunAsync(Request request) { await request.CheckValidityAsync(dbContext); var card = await dbContext.Cards .Include(card => card.Images) .ThenInclude(img => img.Image) .ThenInclude(img => img.Owner) .Include(card => card.CardLanguage) .Include(card => card.TagsInCards) .ThenInclude(tagInCard => tagInCard.Tag) .Include(card => card.UsersWithView) .Where(card => card.Id == request.CardId) .AsSingleQuery() .SingleOrDefaultAsync(); if (card == null) { throw new RequestInputException("Card not found in database"); } var ratings = CardRatings.Load(dbContext, request.CurrentUserId, ImmutableHashSet.Create(request.CardId)); var ownersOfDecksWithThisCard = dbContext.CardsInDecks .Where(cardInDeck => cardInDeck.CardId == request.CardId) .Select(cardInDeck => cardInDeck.Deck.Owner.UserName) .Distinct(); return(new ResultModel( card.FrontSide, card.BackSide, card.AdditionalInfo, card.CardLanguage.Id, card.TagsInCards.Select(tagInCard => new ResultTagModel(tagInCard.TagId, tagInCard.Tag.Name)), card.UsersWithView.Select(userWithView => new ResultUserModel(userWithView.UserId, userWithView.User.UserName)), card.InitialCreationUtcDate, card.VersionUtcDate, ownersOfDecksWithThisCard, card.Images.Select(img => new ResultImageModel(img)), ratings.User(request.CardId), ratings.Average(request.CardId), ratings.Count(request.CardId) )); }
private IEnumerable <ResultCard> GetCardsToRepeat(Guid userId, Guid deckId, IEnumerable <Guid> excludedCardIds, IEnumerable <Guid> excludedTagIds, HeapingAlgorithm heapingAlgorithm, ImmutableDictionary <Guid, string> userNames, ImmutableDictionary <Guid, ImageDetails> imagesDetails, ImmutableDictionary <Guid, string> tagNames, int cardCount) { var result = new List <ResultCard>(); for (var heap = MoveCardToHeap.MaxTargetHeapId; heap > 0 && result.Count < cardCount; heap--) { var cardsOfHeap = dbContext.CardsInDecks.AsNoTracking().Where(cardInDeck => cardInDeck.DeckId == deckId && cardInDeck.CurrentHeap == heap); var withoutExcuded = cardsOfHeap.Where(cardInDeck => !excludedCardIds.Contains(cardInDeck.CardId)); withoutExcuded = withoutExcuded.Where(cardInDeck => !cardInDeck.Card.TagsInCards.Any(tag => excludedTagIds.Contains(tag.TagId))); var ordered = withoutExcuded.OrderBy(cardInDeck => cardInDeck.LastLearnUtcTime); var oldest = ordered.Take(cardCount); var withInfoToComputeExpiration = oldest.Select(cardInDeck => new { cardInDeck.CardId, cardInDeck.CurrentHeap, cardInDeck.LastLearnUtcTime, }).ToList(); var expired = withInfoToComputeExpiration.Where(resultCard => heapingAlgorithm.HasExpired(resultCard.CurrentHeap, resultCard.LastLearnUtcTime)).Select(card => card.CardId).ToList(); var withDetails = dbContext.CardsInDecks.AsNoTracking().Where(cardInDeck => cardInDeck.DeckId == deckId && expired.Contains(cardInDeck.CardId)) .Include(cardInDeck => cardInDeck.Card).AsSingleQuery() .Select(cardInDeck => new { cardInDeck.CardId, cardInDeck.CurrentHeap, cardInDeck.LastLearnUtcTime, cardInDeck.AddToDeckUtcTime, cardInDeck.BiggestHeapReached, cardInDeck.NbTimesInNotLearnedHeap, cardInDeck.Card.FrontSide, cardInDeck.Card.BackSide, cardInDeck.Card.AdditionalInfo, cardInDeck.Card.VersionUtcDate, VersionCreator = cardInDeck.Card.VersionCreator.Id, tagIds = cardInDeck.Card.TagsInCards.Select(tag => tag.TagId), userWithViewIds = cardInDeck.Card.UsersWithView.Select(u => u.UserId), imageIdAndCardSides = cardInDeck.Card.Images.Select(img => new { img.ImageId, img.CardSide }) }).ToList(); var cardIds = expired.ToImmutableHashSet(); var ratings = CardRatings.Load(dbContext, userId, cardIds); var notifications = GetNotifications(userId, cardIds); var thisHeapResult = withDetails.Select(oldestCard => new ResultCard(oldestCard.CardId, oldestCard.CurrentHeap, oldestCard.LastLearnUtcTime, oldestCard.AddToDeckUtcTime, oldestCard.BiggestHeapReached, oldestCard.NbTimesInNotLearnedHeap, oldestCard.FrontSide, oldestCard.BackSide, oldestCard.AdditionalInfo, oldestCard.VersionUtcDate, userNames[oldestCard.VersionCreator], oldestCard.tagIds.Select(tagId => tagNames[tagId]), oldestCard.userWithViewIds.Select(userWithView => userNames[userWithView]), oldestCard.imageIdAndCardSides.Select(imageIdAndCardSide => new ResultImageModel(imagesDetails[imageIdAndCardSide.ImageId], imageIdAndCardSide.CardSide)), heapingAlgorithm, ratings.User(oldestCard.CardId), ratings.Average(oldestCard.CardId), ratings.Count(oldestCard.CardId), notifications[oldestCard.CardId] ) ); result.AddRange(thisHeapResult); } return(result); }
public ResultCardBeforeDeckInfo(Guid cardId, string frontSide, IEnumerable <string> tags, IEnumerable <string> visibleTo, CardRatings cardRatings) { CardId = cardId; FrontSide = frontSide; Tags = tags; VisibleTo = visibleTo; CurrentUserRating = cardRatings.User(cardId); AverageRating = cardRatings.Average(cardId); CountOfUserRatings = cardRatings.Count(cardId); }
private async Task <IEnumerable <ResultCard> > GetUnknownCardsAsync(Guid userId, Guid deckId, IEnumerable <Guid> excludedCardIds, IEnumerable <Guid> excludedTagIds, HeapingAlgorithm heapingAlgorithm, ImmutableDictionary <Guid, string> userNames, ImmutableDictionary <Guid, ImageDetails> imagesDetails, ImmutableDictionary <Guid, string> tagNames, int cardCount) { var cardsOfDeck = dbContext.CardsInDecks.AsNoTracking() .Include(card => card.Card).AsSingleQuery() .Where(card => card.DeckId.Equals(deckId) && !excludedCardIds.Contains(card.CardId)); var onUnknownHeap = cardsOfDeck.Where(cardInDeck => cardInDeck.CurrentHeap == 0); var withoutExcludedCards = onUnknownHeap; foreach (var tag in excludedTagIds) //I tried to do better with an intersect between the two sets, but that failed { withoutExcludedCards = withoutExcludedCards.Where(cardInDeck => !cardInDeck.Card.TagsInCards.Where(tagInCard => tagInCard.TagId == tag).Any()); } var oldest = withoutExcludedCards.OrderBy(cardInDeck => cardInDeck.LastLearnUtcTime).Take(cardCount * 3); //we take more cards for shuffling accross more var withDetails = oldest.Select(cardInDeck => new { cardInDeck.CardId, cardInDeck.LastLearnUtcTime, cardInDeck.AddToDeckUtcTime, cardInDeck.BiggestHeapReached, cardInDeck.NbTimesInNotLearnedHeap, cardInDeck.Card.FrontSide, cardInDeck.Card.BackSide, cardInDeck.Card.AdditionalInfo, cardInDeck.Card.VersionUtcDate, VersionCreator = cardInDeck.Card.VersionCreator.Id, tagIds = cardInDeck.Card.TagsInCards.Select(tag => tag.TagId), userWithViewIds = cardInDeck.Card.UsersWithView.Select(u => u.UserId), imageIdAndCardSides = cardInDeck.Card.Images.Select(img => new { img.ImageId, img.CardSide }) }); var listed = await withDetails.ToListAsync(); var cardIds = listed.Select(cardInDeck => cardInDeck.CardId).ToImmutableHashSet(); var ratings = CardRatings.Load(dbContext, userId, cardIds); var notifications = GetNotifications(userId, cardIds); var result = listed.Select(cardInDeck => new ResultCard( cardInDeck.CardId, 0, cardInDeck.LastLearnUtcTime, cardInDeck.AddToDeckUtcTime, cardInDeck.BiggestHeapReached, cardInDeck.NbTimesInNotLearnedHeap, cardInDeck.FrontSide, cardInDeck.BackSide, cardInDeck.AdditionalInfo, cardInDeck.VersionUtcDate, userNames[cardInDeck.VersionCreator], cardInDeck.tagIds.Select(tagId => tagNames[tagId]), cardInDeck.userWithViewIds.Select(userWithView => userNames[userWithView]), cardInDeck.imageIdAndCardSides.Select(imageIdAndCardSide => new ResultImageModel(imagesDetails[imageIdAndCardSide.ImageId], imageIdAndCardSide.CardSide)), heapingAlgorithm, ratings.User(cardInDeck.CardId), ratings.Average(cardInDeck.CardId), ratings.Count(cardInDeck.CardId), notifications[cardInDeck.CardId] )); return(Shuffler.Shuffle(result).Take(cardCount)); }
public Result Run(Request request, Guid userId) { var allCards = dbContext.Cards.AsNoTracking() .Include(card => card.TagsInCards) .Include(card => card.VersionCreator) .Include(card => card.CardLanguage) .Include(card => card.UsersWithView) .AsSingleQuery(); var cardsViewableByUser = allCards.Where( card => card.VersionCreator.Id == userId || !card.UsersWithView.Any() || //card is public card.UsersWithView.Where(userWithView => userWithView.UserId == userId).Any() ); IQueryable <Card> cardsFilteredWithDeck; if (request.Deck != Guid.Empty) { if (request.DeckIsInclusive) { cardsFilteredWithDeck = cardsViewableByUser.Where(card => dbContext.CardsInDecks.AsNoTracking().Where(cardInDeck => cardInDeck.CardId == card.Id && cardInDeck.DeckId == request.Deck).Any() && (request.Heap == -1 || dbContext.CardsInDecks.AsNoTracking().Single(cardInDeck => cardInDeck.CardId == card.Id && cardInDeck.DeckId == request.Deck).CurrentHeap == request.Heap) ); } else { cardsFilteredWithDeck = cardsViewableByUser.Where(card => !dbContext.CardsInDecks.AsNoTracking().Where(cardInDeck => cardInDeck.CardId == card.Id && cardInDeck.DeckId == request.Deck).Any()); } } else { cardsFilteredWithDeck = cardsViewableByUser; } var cardsFilteredWithRequiredTags = cardsFilteredWithDeck; foreach (var tag in request.RequiredTags) //I tried to do better with an intersect between the two sets, but that failed { cardsFilteredWithRequiredTags = cardsFilteredWithRequiredTags.Where(card => card.TagsInCards.Where(tagInCard => tagInCard.TagId == tag).Any()); } var cardsFilteredWithExludedTags = cardsFilteredWithRequiredTags; if (request.ExcludedTags == null) { cardsFilteredWithExludedTags = cardsFilteredWithExludedTags.Where(card => !card.TagsInCards.Any()); } else { foreach (var tag in request.ExcludedTags) //I tried to do better with an intersect between the two sets, but that failed { cardsFilteredWithExludedTags = cardsFilteredWithExludedTags.Where(card => !card.TagsInCards.Where(tagInCard => tagInCard.TagId == tag).Any()); } } var cardsFilteredWithText = string.IsNullOrEmpty(request.RequiredText) ? cardsFilteredWithExludedTags : cardsFilteredWithExludedTags.Where(card => EF.Functions.Like(card.FrontSide, $"%{request.RequiredText}%") || EF.Functions.Like(card.BackSide, $"%{request.RequiredText}%") || EF.Functions.Like(card.AdditionalInfo, $"%{request.RequiredText}%") ); IQueryable <Card> cardsFilteredWithVisibility; if (request.Visibility == 2) { cardsFilteredWithVisibility = cardsFilteredWithText.Where(card => card.UsersWithView.Count() != 1); } else if (request.Visibility == 3) { cardsFilteredWithVisibility = cardsFilteredWithText.Where(card => card.UsersWithView.Count() == 1); } else { cardsFilteredWithVisibility = cardsFilteredWithText; } IQueryable <Card> cardsFilteredWithAverageRating; CardRatings? cardRatings = null; if (request.RatingFilteringMode == 1) { cardsFilteredWithAverageRating = cardsFilteredWithVisibility; } else { if (cardsFilteredWithVisibility.Count() > 20000) { throw new SearchResultTooBigForRatingException(); } cardRatings = CardRatings.Load(dbContext, userId, cardsFilteredWithVisibility.Select(card => card.Id).ToImmutableHashSet()); if (request.RatingFilteringMode == 4) { cardsFilteredWithAverageRating = cardsFilteredWithVisibility.Where(card => cardRatings.CardsWithoutEval.Contains(card.Id)); } else if (request.RatingFilteringMode == 2) { cardsFilteredWithAverageRating = cardsFilteredWithVisibility.Where(card => cardRatings.CardsWithAverageRatingAtLeast(request.RatingFilteringValue).Contains(card.Id)); } else { cardsFilteredWithAverageRating = cardsFilteredWithVisibility.Where(card => cardRatings.CardsWithAverageRatingAtMost(request.RatingFilteringValue).Contains(card.Id)); } } IQueryable <Card> cardsFilteredWithNotifications; if (request.NotificationFiltering == 1) { cardsFilteredWithNotifications = cardsFilteredWithAverageRating; } else { var notifMustExist = request.NotificationFiltering == 2; cardsFilteredWithNotifications = cardsFilteredWithAverageRating.Where(card => dbContext.CardNotifications.AsNoTracking().Where(cardNotif => cardNotif.CardId == card.Id && cardNotif.UserId == userId).Any() == notifMustExist); } var finalResult = cardsFilteredWithNotifications; finalResult = finalResult.OrderByDescending(card => card.VersionUtcDate); //For Take() and Skip(), just below, to work, we need to have an order. In future versions we will offer the user some sorting var totalNbCards = finalResult.Count(); var totalPageCount = (int)Math.Ceiling(((double)totalNbCards) / request.pageSize); var pageCards = finalResult.Skip((request.pageNo - 1) * request.pageSize).Take(request.pageSize); if (cardRatings == null) { cardRatings = CardRatings.Load(dbContext, userId, pageCards.Select(card => card.Id).ToImmutableHashSet()); } var resultCards = pageCards.Select(card => new ResultCardBeforeDeckInfo(card.Id, card.FrontSide, card.TagsInCards.Select(tagInCard => tagInCard.Tag.Name), card.UsersWithView.Select(userWithView => userWithView.User.UserName), cardRatings)).ToList(); var withUserDeckInfo = AddDeckInfo(userId, resultCards); return(new Result(totalNbCards, totalPageCount, withUserDeckInfo)); }