public async ValueTask <ActionResult <TextDto> > GetById(string textId) { var text = await _textRepository.FindAsync(textId); if (text == null || text.IsArchived) { return(NotFound()); } return(TextDto.From(text)); }
public async ValueTask AddTypingResultAsync(string userSessionId, TextTypingResult textTypingResult) { var userSession = await _userSessionRepository.FindAsync(userSessionId) .ConfigureAwait(false); if (userSession == null) { throw new InvalidOperationException("User session does not exist."); } // TODO: Validate that it is currently active session. var typingSession = await _typingSessionRepository.FindAsync(userSession.TypingSessionId) .ConfigureAwait(false); if (typingSession == null) { throw new InvalidOperationException("Typing session does not exist."); } // TODO: Validate that it is type-able (active, etc). var typingSessionText = typingSession.GetTypingSessionTextAtIndexOrDefault( textTypingResult.TypingSessionTextIndex); if (typingSessionText == null) { throw new InvalidOperationException("Typing session text with this index does not exist in typing session."); } var text = await _textRepository.FindAsync(typingSessionText.TextId) .ConfigureAwait(false); if (text == null) { throw new InvalidOperationException("Text with such ID does not exist."); } if (typingSessionText.Value != text.Value) { throw new InvalidOperationException("Typing session text value differs from the one from the text store. Corrupted state."); } // If this doesn't throw - validation succeeded. await _textTypingResultValidator.ValidateAsync(text.Value, textTypingResult) .ConfigureAwait(false); userSession.LogResult(textTypingResult); await _userSessionRepository.SaveAsync(userSession) .ConfigureAwait(false); }
public async ValueTask <ActionResult> AddText(string typingSessionId, AddTextToTypingSessionDto dto) { var typingSession = await _typingSessionRepository.FindAsync(typingSessionId); if (typingSession == null) { return(NotFound()); } // TODO: Validate that the session was created by this user or that it's public / shared, otherwise return NotFound. var text = await _textRepository.FindAsync(dto.TextId); if (text == null) { return(NotFound()); } var textIndex = typingSession.AddText(new TypingSessionText(dto.TextId, text.Value)); await _typingSessionRepository.SaveAsync(typingSession); var result = new { typingSessionId = typingSessionId, textIndex = textIndex }; return(CreatedAtAction(nameof(GetTextInTypingSession), result, result)); }
public async ValueTask <UserTypingStatistics> GenerateUserStatisticsAsync(string userId, string language, TextGenerationType textGenerationType) { var existingStatistics = await _userTypingStatisticsStore.GetUserTypingStatisticsAsync(userId, language, textGenerationType) .ConfigureAwait(false); var userSessions = Enumerable.Empty <UserSession>(); userSessions = existingStatistics != null ? await _userSessionRepository.FindAllForUserFromTypingResultsAsync(userId, existingStatistics.LastHandledResultUtc) .ConfigureAwait(false) : await _userSessionRepository.FindAllForUserAsync(userId) .ConfigureAwait(false); var results = new List <TextAnalysisResult>(); var textsTypedCount = 0; DateTime lastHandledResultUtc = default; foreach (var userSession in userSessions) { var typingSession = await _typingSessionRepository.FindAsync(userSession.TypingSessionId) .ConfigureAwait(false); if (typingSession == null) { throw new InvalidOperationException("Typing session is not found."); } foreach (var textTypingResult in userSession.GetTextTypingResults()) { textsTypedCount++; if (textTypingResult.SubmittedResultsUtc > lastHandledResultUtc) { lastHandledResultUtc = textTypingResult.SubmittedResultsUtc; } var text = typingSession.GetTypingSessionTextAtIndexOrDefault(textTypingResult.TypingSessionTextIndex); if (text == null) { throw new InvalidOperationException("Text is not found in typing session."); } var textEntity = await _textRepository.FindAsync(text.TextId) .ConfigureAwait(false); if (textEntity == null) { throw new InvalidOperationException("Text is not found."); } if (textEntity.Language != language) { continue; } if (textEntity.TextGenerationType != textGenerationType) { continue; // Generate statistics only for requested text generation type. } var textAnalysisResult = await _textTypingResultValidator.ValidateAsync(text.Value, textTypingResult) .ConfigureAwait(false); results.Add(textAnalysisResult); } } if (results.Count == 0) { if (existingStatistics != null) { // No new data yet. return(existingStatistics); } return(new UserTypingStatistics(0, 0, Enumerable.Empty <KeyPairAggregatedData>(), DateTime.UtcNow)); } var aggregatedResult = new TextAnalysisResult( results.Sum(x => x.SpeedCpm) / results.Count, results.SelectMany(x => x.KeyPairs)); var specificKeys = aggregatedResult.KeyPairs.GroupBy(x => new { x.FromKey, x.ShouldBeKey }); var aggregatedData = specificKeys.Select(x => new KeyPairAggregatedData( x.Key.FromKey, x.Key.ShouldBeKey, x.Where(y => y.Type == KeyPairType.Correct).Any() ? x.Where(y => y.Type == KeyPairType.Correct).Average(y => y.Delay) : 0, x.Where(y => y.Type == KeyPairType.Correct).Any() ? x.Where(y => y.Type == KeyPairType.Correct).Min(y => y.Delay) : 0, x.Where(y => y.Type == KeyPairType.Correct).Any() ? x.Where(y => y.Type == KeyPairType.Correct).Max(y => y.Delay) : 0, x.Count(y => y.Type == KeyPairType.Correct), x.Count(y => y.Type == KeyPairType.Mistake))); var result = MergeAndReturnNew(existingStatistics, new TypingReport(aggregatedResult, aggregatedData), textsTypedCount, lastHandledResultUtc); await _userTypingStatisticsStore.SaveAsync(userId, result, language, textGenerationType) .ConfigureAwait(false); return(result); }