示例#1
0
        public ActionResult UnitStatistics(UnitStatisticsParams param)
        {
            const int usersLimit = 200;

            var courseId     = param.CourseId;
            var unitId       = param.UnitId;
            var periodStart  = param.PeriodStartDate;
            var periodFinish = param.PeriodFinishDate;
            var groupsIds    = Request.GetMultipleValues("group");

            var realPeriodFinish = periodFinish.Add(TimeSpan.FromDays(1));

            var course = courseManager.GetCourse(courseId);

            if (!unitId.HasValue)
            {
                return(View("UnitStatisticsList", new UnitStatisticPageModel
                {
                    Course = course,
                    Units = course.Units,
                }));
            }
            var selectedUnit = course.GetUnitById(unitId.Value);
            var slides       = selectedUnit.Slides;
            var slidesIds    = slides.Select(s => s.Id).ToList();
            var quizzes      = slides.OfType <QuizSlide>();
            var exersices    = slides.OfType <ExerciseSlide>();

            var groups        = groupsRepo.GetAvailableForUserGroups(courseId, User);
            var filterOptions = ControllerUtils.GetFilterOptionsByGroup <VisitsFilterOptions>(groupsRepo, User, courseId, groupsIds);

            filterOptions.SlidesIds    = slidesIds;
            filterOptions.PeriodStart  = periodStart;
            filterOptions.PeriodFinish = realPeriodFinish;

            /* Dictionary<SlideId, List<Visit>> */
            var slidesVisits = visitsRepo.GetVisitsInPeriodForEachSlide(filterOptions);

            var usersVisitedAllSlidesBeforePeriodCount         = visitsRepo.GetUsersVisitedAllSlides(filterOptions.WithPeriodStart(DateTime.MinValue).WithPeriodFinish(periodStart)).Count();
            var usersVisitedAllSlidesInPeriodCount             = visitsRepo.GetUsersVisitedAllSlides(filterOptions).Count();
            var usersVisitedAllSlidesBeforePeriodFinishedCount = visitsRepo.GetUsersVisitedAllSlides(filterOptions.WithPeriodStart(DateTime.MinValue)).Count();

            var quizzesAverageScore = quizzes.ToDictionary(q => q.Id,
                                                           q => (int)slidesVisits.GetOrDefault(q.Id, new List <Visit>())
                                                           .Where(v => v.IsPassed)
                                                           .Select(v => 100 * Math.Min(v.Score, q.MaxScore) / (q.MaxScore != 0 ? q.MaxScore : 1))
                                                           .DefaultIfEmpty(-1)
                                                           .Average()
                                                           );

            /* Dictionary<SlideId, count (distinct by user)> */
            var exercisesSolutionsCount = userSolutionsRepo.GetAllSubmissions(courseId, slidesIds, periodStart, realPeriodFinish)
                                          .GroupBy(s => s.SlideId)
                                          .ToDictionary(g => g.Key, g => g.DistinctBy(s => s.UserId).Count());

            var exercisesAcceptedSolutionsCount = userSolutionsRepo.GetAllAcceptedSubmissions(courseId, slidesIds, periodStart, realPeriodFinish)
                                                  .GroupBy(s => s.SlideId)
                                                  .ToDictionary(g => g.Key, g => g.DistinctBy(s => s.UserId).Count());

            var usersIds = visitsRepo.GetVisitsInPeriod(filterOptions).DistinctBy(v => v.UserId).Select(v => v.UserId);

            /* If we filtered out users from one or several groups show them all */
            if (filterOptions.UsersIds != null && !filterOptions.IsUserIdsSupplement)
            {
                usersIds = filterOptions.UsersIds;
            }
            var visitedUsers = usersIds
                               .Join(db.Users, v => v, u => u.Id, (v, u) => new UnitStatisticUserInfo {
                UserId = u.Id, UserName = u.UserName, UserVisibleName = (u.LastName + u.FirstName != "" ? u.LastName + " " + u.FirstName : u.UserName).Trim()
            })
                               .ToList();
            var isMore = visitedUsers.Count > usersLimit;

            var visitedSlidesCountByUser = visitsRepo.GetVisitsInPeriod(filterOptions)
                                           .GroupBy(v => v.UserId)
                                           .ToDictionary(g => g.Key, g => g.Count());
            var visitedSlidesCountByUserAllTime = visitsRepo.GetVisitsInPeriod(filterOptions.WithPeriodStart(DateTime.MinValue).WithPeriodFinish(DateTime.MaxValue))
                                                  .GroupBy(v => v.UserId)
                                                  .ToDictionary(g => g.Key, g => g.Count());

            /* Get `usersLimit` best by slides count and order them by name */
            visitedUsers = visitedUsers
                           .OrderByDescending(u => visitedSlidesCountByUserAllTime.GetOrDefault(u.UserId, 0))
                           .Take(usersLimit)
                           .OrderBy(u => u.UserVisibleName)
                           .ToList();

            var visitedUsersIds  = visitedUsers.Select(v => v.UserId).ToList();
            var additionalScores = additionalScoresRepo
                                   .GetAdditionalScoresForUsers(courseId, unitId.Value, visitedUsersIds)
                                   .ToDictionary(kv => kv.Key, kv => kv.Value.Score);
            var usersGroupsIds = groupsRepo.GetUsersGroupsIds(courseId, visitedUsersIds);
            var enabledAdditionalScoringGroupsForGroups = groupsRepo.GetEnabledAdditionalScoringGroups(courseId)
                                                          .GroupBy(e => e.GroupId)
                                                          .ToDictionary(g => g.Key, g => g.Select(e => e.ScoringGroupId).ToList());

            var model = new UnitStatisticPageModel
            {
                Course            = course,
                Units             = course.Units,
                Unit              = selectedUnit,
                SelectedGroupsIds = groupsIds,
                Groups            = groups,

                PeriodStart  = periodStart,
                PeriodFinish = periodFinish,

                Slides       = slides,
                SlidesVisits = slidesVisits,

                UsersVisitedAllSlidesBeforePeriodCount         = usersVisitedAllSlidesBeforePeriodCount,
                UsersVisitedAllSlidesInPeriodCount             = usersVisitedAllSlidesInPeriodCount,
                UsersVisitedAllSlidesBeforePeriodFinishedCount = usersVisitedAllSlidesBeforePeriodFinishedCount,

                QuizzesAverageScore             = quizzesAverageScore,
                ExercisesSolutionsCount         = exercisesSolutionsCount,
                ExercisesAcceptedSolutionsCount = exercisesAcceptedSolutionsCount,
                VisitedUsers                    = visitedUsers,
                VisitedUsersIsMore              = isMore,
                VisitedSlidesCountByUser        = visitedSlidesCountByUser,
                VisitedSlidesCountByUserAllTime = visitedSlidesCountByUserAllTime,

                AdditionalScores = additionalScores,
                UsersGroupsIds   = usersGroupsIds,
                EnabledAdditionalScoringGroupsForGroups = enabledAdditionalScoringGroupsForGroups,
            };

            return(View(model));
        }
示例#2
0
        private StudentSubmissionsModel GetStudentSubmissionsModel(string courseId, Guid slideId, string name)
        {
            const int maxUsersCount = 30;

            var course = courseManager.GetCourse(courseId);
            var slide  = course.GetSlideById(slideId) as ExerciseSlide;

            if (slide == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            var canViewAllSubmissions = User.HasAccessFor(courseId, CourseRole.CourseAdmin) || User.HasCourseAccess(courseId, CourseAccessType.ViewAllStudentsSubmissions);
            var hasFilterByName       = !string.IsNullOrEmpty(name);

            /* By default show members of `my` groups, but if filter is enabled course admin's and users with special access can view any student's submissions */

            SubmissionsFilterOptions filterOptions;
            var slideIdInList = new List <Guid> {
                slideId
            };
            var visitedUserIds = visitsRepo.GetVisitsInPeriod(new VisitsFilterOptions {
                CourseId = courseId, SlidesIds = slideIdInList, PeriodStart = DateTime.MinValue, PeriodFinish = DateTime.MaxValue
            })
                                 .Select(v => v.UserId)
                                 .ToList();

            if (hasFilterByName && canViewAllSubmissions)
            {
                /* Get all members who has visits to this slide */
                filterOptions = new SubmissionsFilterOptions
                {
                    CourseId = courseId,
                    UserIds  = visitedUserIds,
                    SlideIds = slideIdInList,
                };
            }
            else
            {
                /* Get members of `my` groups */
                filterOptions          = ControllerUtils.GetFilterOptionsByGroup <SubmissionsFilterOptions>(groupsRepo, User, courseId, groupsIds: new List <string>());
                filterOptions.SlideIds = slideIdInList;
                /* Filter out only users with visits to this slide */
                filterOptions.UserIds = filterOptions.UserIds.Intersect(visitedUserIds).ToList();
            }

            if (hasFilterByName)
            {
                var filteredUserIds = usersRepo.FilterUsersByNamePrefix(name);
                filterOptions.UserIds = filterOptions.UserIds.Intersect(filteredUserIds).ToList();
            }

            filterOptions.UserIds = filterOptions.UserIds.Take(maxUsersCount).ToList();

            var submissions       = userSolutionsRepo.GetAllSubmissionsByUsers(filterOptions);
            var submissionsByUser = submissions.GroupBy(s => s.UserId).ToDictionary(g => g.Key, g => g.ToList()).ToDefaultDictionary();

            var automaticCheckingScores = slideCheckingsRepo.GetAutomaticScoresForSlide(courseId, slideId, filterOptions.UserIds);
            var manualCheckingScores    = slideCheckingsRepo.GetManualScoresForSlide(courseId, slideId, filterOptions.UserIds);

            var userGroups = groupsRepo.GetUsersGroupsNamesAsStrings(courseId, filterOptions.UserIds, User).ToDefaultDictionary();

            return(new StudentSubmissionsModel
            {
                CourseId = courseId,
                Slide = slide,
                Users = usersRepo.GetUsersByIds(filterOptions.UserIds).ToDictionary(u => u.Id),
                SubmissionsByUser = submissionsByUser,
                AutomaticCheckingScores = automaticCheckingScores,
                ManualCheckingScores = manualCheckingScores,
                HasFilterByName = hasFilterByName,
                UserGroups = userGroups,
            });
        }
示例#3
0
        /*TODO: extract copy-paste */
        private CourseStatisticPageModel GetCourseStatisticsModel(CourseStatisticsParams param, int usersLimit)
        {
            var courseId     = param.CourseId;
            var periodStart  = param.PeriodStartDate;
            var periodFinish = param.PeriodFinishDate;
            var groupsIds    = Request.GetMultipleValues("group");
            var isInstructor = User.HasAccessFor(courseId, CourseRole.Instructor);
            var isStudent    = !isInstructor;

            var currentUserId = User.Identity.GetUserId();

            if (isStudent && !CanStudentViewGroupsStatistics(currentUserId, groupsIds))
            {
                return(null);
            }

            var realPeriodFinish = periodFinish.Add(TimeSpan.FromDays(1));

            var course    = courseManager.GetCourse(courseId);
            var slidesIds = course.Slides.Select(s => s.Id).ToList();

            var filterOptions = ControllerUtils.GetFilterOptionsByGroup <VisitsFilterOptions>(groupsRepo, User, courseId, groupsIds, allowSeeGroupForAnyMember: true);

            filterOptions.SlidesIds    = slidesIds;
            filterOptions.PeriodStart  = periodStart;
            filterOptions.PeriodFinish = realPeriodFinish;

            var usersIds = visitsRepo.GetVisitsInPeriod(filterOptions).DistinctBy(v => v.UserId).Select(v => v.UserId);

            /* If we filtered out users from one or several groups show them all */
            if (filterOptions.UsersIds != null && !filterOptions.IsUserIdsSupplement)
            {
                usersIds = filterOptions.UsersIds;
            }

            var visitedUsers = usersIds
                               .Join(db.Users, v => v, u => u.Id, (v, u) => new UnitStatisticUserInfo {
                UserId = u.Id, UserName = u.UserName, UserVisibleName = (u.LastName + u.FirstName != "" ? u.LastName + " " + u.FirstName : u.UserName).Trim()
            })
                               .ToList();
            var isMore = visitedUsers.Count > usersLimit;

            var unitBySlide   = course.Units.SelectMany(u => u.Slides.Select(s => Tuple.Create(u.Id, s.Id))).ToDictionary(p => p.Item2, p => p.Item1);
            var scoringGroups = course.Settings.Scoring.Groups;

            var visitedSlidesCountByUserAllTime = visitsRepo.GetVisitsInPeriod(filterOptions.WithPeriodStart(DateTime.MinValue).WithPeriodFinish(DateTime.MaxValue))
                                                  .GroupBy(v => v.UserId)
                                                  .ToDictionary(g => g.Key, g => g.Count());

            /* Get `usersLimit` best by slides count */
            visitedUsers = visitedUsers
                           .OrderByDescending(u => visitedSlidesCountByUserAllTime.GetOrDefault(u.UserId, 0))
                           .Take(usersLimit)
                           .ToList();
            var visitedUsersIds = visitedUsers.Select(v => v.UserId).ToList();

            var visitedUsersGroups = groupsRepo.GetUsersGroupsIds(new List <string> {
                courseId
            }, visitedUsersIds, User, 10).ToDefaultDictionary();

            /* From now fetch only filtered users' statistics */
            filterOptions.UsersIds            = visitedUsersIds;
            filterOptions.IsUserIdsSupplement = false;
            var scoreByUserUnitScoringGroup = ((IEnumerable <Visit>)visitsRepo.GetVisitsInPeriod(filterOptions))
                                              .GroupBy(v => Tuple.Create(v.UserId, unitBySlide[v.SlideId], course.FindSlideById(v.SlideId)?.ScoringGroup))
                                              .ToDictionary(g => g.Key, g => g.Sum(v => v.Score))
                                              .ToDefaultDictionary();

            var shouldBeSolvedSlides    = course.Slides.Where(s => s.ShouldBeSolved).ToList();
            var shouldBeSolvedSlidesIds = shouldBeSolvedSlides.Select(s => s.Id).ToList();
            var shouldBeSolvedSlidesByUnitScoringGroup = shouldBeSolvedSlides
                                                         .GroupBy(s => Tuple.Create(unitBySlide[s.Id], s.ScoringGroup))
                                                         .ToDictionary(g => g.Key, g => g.ToList())
                                                         .ToDefaultDictionary();
            var scoreByUserAndSlide = ((IEnumerable <Visit>)visitsRepo.GetVisitsInPeriod(filterOptions.WithSlidesIds(shouldBeSolvedSlidesIds)))
                                      .GroupBy(v => Tuple.Create(v.UserId, v.SlideId))
                                      .ToDictionary(g => g.Key, g => g.Sum(v => v.Score))
                                      .ToDefaultDictionary();

            var additionalScores = additionalScoresRepo
                                   .GetAdditionalScoresForUsers(courseId, visitedUsersIds)
                                   .ToDictionary(kv => kv.Key, kv => kv.Value.Score)
                                   .ToDefaultDictionary();
            var usersGroupsIds = groupsRepo.GetUsersGroupsIds(courseId, visitedUsersIds);
            var enabledAdditionalScoringGroupsForGroups = groupsRepo.GetEnabledAdditionalScoringGroups(courseId)
                                                          .GroupBy(e => e.GroupId)
                                                          .ToDictionary(g => g.Key, g => g.Select(e => e.ScoringGroupId).ToList());

            /* Filter out only scoring groups which are affected in selected groups */
            var additionalScoringGroupsForFilteredGroups = ControllerUtils.GetEnabledAdditionalScoringGroupsForGroups(groupsRepo, course, groupsIds, User);

            scoringGroups = scoringGroups
                            .Where(kv => kv.Value.MaxNotAdditionalScore > 0 || additionalScoringGroupsForFilteredGroups.Contains(kv.Key))
                            .ToDictionary(kv => kv.Key, kv => kv.Value)
                            .ToSortedDictionary();

            var groups = groupsRepo.GetAvailableForUserGroups(courseId, User);

            if (!isInstructor)
            {
                groups = groupsRepo.GetUserGroups(courseId, currentUserId);
            }
            var model = new CourseStatisticPageModel
            {
                IsInstructor       = isInstructor,
                Course             = course,
                SelectedGroupsIds  = groupsIds,
                Groups             = groups,
                PeriodStart        = periodStart,
                PeriodFinish       = periodFinish,
                VisitedUsers       = visitedUsers,
                VisitedUsersIsMore = isMore,
                VisitedUsersGroups = visitedUsersGroups,
                ShouldBeSolvedSlidesByUnitScoringGroup = shouldBeSolvedSlidesByUnitScoringGroup,
                ScoringGroups = scoringGroups,
                ScoreByUserUnitScoringGroup = scoreByUserUnitScoringGroup,
                ScoreByUserAndSlide         = scoreByUserAndSlide,
                AdditionalScores            = additionalScores,
                UsersGroupsIds = usersGroupsIds,
                EnabledAdditionalScoringGroupsForGroups = enabledAdditionalScoringGroupsForGroups,
            };

            return(model);
        }
示例#4
0
 /* Copy&paste from AdminController */
 private ManualCheckingQueueFilterOptions GetManualCheckingFilterOptionsByGroup(string courseId, List <string> groupsIds)
 {
     return(ControllerUtils.GetFilterOptionsByGroup <ManualCheckingQueueFilterOptions>(groupsRepo, User, courseId, groupsIds));
 }