public async Task <ActionResult <UsersProgressResponse> > UserProgress([FromRoute] string courseId, [FromBody] UserProgressParameters parameters) { if (!await courseManager.HasCourseAsync(courseId)) { return(NotFound(new ErrorResponse($"Course {courseId} not found"))); } var course = await courseManager.FindCourseAsync(courseId); var userIds = parameters.UserIds; if (userIds == null || userIds.Count == 0) { userIds = new List <string> { UserId } } ; else { var userIdsWithProgressNotVisibleForUser = await GetUserIdsWithProgressNotVisibleForUser(course.Id, userIds); if (userIdsWithProgressNotVisibleForUser?.Any() ?? false) { var userIdsStr = string.Join(", ", userIdsWithProgressNotVisibleForUser); return(NotFound(new ErrorResponse($"Users {userIdsStr} not found"))); } } var isInstructor = await courseRolesRepo.HasUserAccessToCourseAsync(UserId, courseId, CourseRoleType.Instructor).ConfigureAwait(false); var visibleSlides = course.GetSlides(isInstructor).Select(s => s.Id).ToHashSet(); var scores = await visitsRepo.GetScoresForSlides(course.Id, userIds); var visitsTimestamps = await visitsRepo.GetLastVisitsInCourse(course.Id, UserId); var additionalScores = await GetAdditionalScores(course.Id, userIds).ConfigureAwait(false); var attempts = await userQuizzesRepo.GetUsedAttemptsCountAsync(course.Id, userIds).ConfigureAwait(false); var waitingQuizSlides = await userQuizzesRepo.GetSlideIdsWaitingForManualCheckAsync(course.Id, userIds).ConfigureAwait(false); var waitingExerciseSlides = await slideCheckingsRepo.GetSlideIdsWaitingForManualExerciseCheckAsync(course.Id, userIds).ConfigureAwait(false); var prohibitFurtherManualCheckingSlides = await slideCheckingsRepo.GetProhibitFurtherManualCheckingSlides(course.Id, userIds).ConfigureAwait(false); var skippedSlides = await visitsRepo.GetSkippedSlides(course.Id, userIds); var usersProgress = new Dictionary <string, UserProgress>(); foreach (var userId in scores.Keys) { var visitedSlides = scores[userId] .Where(kvp => visibleSlides.Contains(kvp.Key)) .ToDictionary(kvp => kvp.Key, kvp => new UserProgressSlideResult { Visited = true, Timestamp = visitsTimestamps.TryGetValue(kvp.Key, out var visit) ? visit.Timestamp : null, Score = kvp.Value, IsSkipped = skippedSlides.GetValueOrDefault(userId)?.Contains(kvp.Key) ?? false, UsedAttempts = attempts.GetValueOrDefault(userId)?.GetValueOrDefault(kvp.Key) ?? 0, WaitingForManualChecking = (waitingExerciseSlides.GetValueOrDefault(userId)?.Contains(kvp.Key) ?? false) || (waitingQuizSlides.GetValueOrDefault(userId)?.Contains(kvp.Key) ?? false), ProhibitFurtherManualChecking = prohibitFurtherManualCheckingSlides.GetValueOrDefault(userId)?.Contains(kvp.Key) ?? false });