Beispiel #1
0
        public async Task <ActionResult> DropQuiz(string courseId, Guid slideId, bool isLti)
        {
            var slide = courseManager.GetCourse(courseId).GetSlideById(slideId);

            if (slide is QuizSlide)
            {
                var userId              = User.Identity.GetUserId();
                var userQuizDrops       = userQuizzesRepo.GetQuizDropStates(courseId, userId, slideId).Count(b => b);
                var maxTriesCount       = GetMaxTriesCount(courseId, slide as QuizSlide);
                var isQuizScoredMaximum = userQuizzesRepo.IsQuizScoredMaximum(courseId, userId, slideId);
                if (userQuizDrops + 1 < maxTriesCount && !isQuizScoredMaximum)
                {
                    await userQuizzesRepo.DropQuizAsync(courseId, userId, slideId);

                    await visitsRepo.UpdateScoreForVisit(courseId, slideId, userId);

                    if (isLti)
                    {
                        LtiUtils.SubmitScore(courseId, slide, userId);
                    }
                }
                else if ((userQuizDrops + 1 >= maxTriesCount || isQuizScoredMaximum) && !(slide as QuizSlide).ManualChecking)
                {
                    /* Allow user to drop quiz after all tries are exceeded, but don't update score */
                    await userQuizzesRepo.DropQuizAsync(courseId, userId, slideId);
                }
            }
            var model = new { courseId, slideId = slide.Id, isLti };

            if (isLti)
            {
                return(RedirectToAction("LtiSlide", "Course", model));
            }
            return(RedirectToAction("SlideById", "Course", model));
        }
Beispiel #2
0
        private static async Task ResendLti(UlearnDb db)
        {
            var ltiConsumersRepo   = new LtiConsumersRepo(db);
            var slideCheckingsRepo = new SlideCheckingsRepo(db, null);
            var visitsRepo         = new VisitsRepo(db, slideCheckingsRepo);
            // current 288064
            var ltiRequests = await db.LtiRequests.Where(r => r.RequestId > 285417).OrderByDescending(r => r.RequestId).ToListAsync();

            var courseManager = new CourseManager(CourseManager.GetCoursesDirectory());

            var i = 0;

            foreach (var ltiRequest in ltiRequests)
            {
                i++;
                Console.WriteLine($"{i} requestId {ltiRequest.RequestId}");
                try
                {
                    var course = courseManager.GetCourse(ltiRequest.CourseId);
                    var slide  = course.GetSlideById(ltiRequest.SlideId, true);
                    var score  = await visitsRepo.GetScore(ltiRequest.CourseId, ltiRequest.SlideId, ltiRequest.UserId);

                    await LtiUtils.SubmitScore(slide, ltiRequest.UserId, score, ltiRequest.Request, ltiConsumersRepo);

                    Console.WriteLine($"{i} requestId {ltiRequest.RequestId} score {score}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
        }
Beispiel #3
0
        public async Task <ActionResult> DropQuiz(string courseId, Guid slideId, bool isLti)
        {
            var slide = courseManager.GetCourse(courseId).GetSlideById(slideId);

            if (slide is QuizSlide)
            {
                var userId = User.Identity.GetUserId();
                if (userQuizzesRepo.GetQuizDropStates(courseId, userId, slideId).Count(b => b) < GetMaxDropCount(slide as QuizSlide) &&
                    !userQuizzesRepo.IsQuizScoredMaximum(courseId, userId, slideId))
                {
                    await userQuizzesRepo.DropQuiz(userId, slideId);

                    await slideCheckingsRepo.RemoveAttempts(courseId, slideId, userId);

                    await visitsRepo.UpdateScoreForVisit(courseId, slideId, userId);

                    if (isLti)
                    {
                        LtiUtils.SubmitScore(slide, userId);
                    }
                }
            }
            var model = new { courseId, slideId = slide.Id, isLti };

            if (isLti)
            {
                return(RedirectToAction("LtiSlide", "Course", model));
            }
            return(RedirectToAction("SlideById", "Course", model));
        }
Beispiel #4
0
        public async Task <ActionResult> DropQuiz(string courseId, string slideId, bool isLti)
        {
            var slide = courseManager.GetCourse(courseId).GetSlideById(slideId);

            if (slide is QuizSlide)
            {
                var userId = User.Identity.GetUserId();
                if (userQuizzesRepo.GetQuizDropStates(courseId, userId, slideId).Count(b => b) < GetMaxDropCount(slide as QuizSlide) &&
                    !userQuizzesRepo.GetQuizBlocksTruth(courseId, userId, slideId).All(b => b.Value))
                {
                    await userQuizzesRepo.DropQuiz(userId, slideId);

                    await visitsRepo.DropAttempt(slideId, userId);

                    if (isLti)
                    {
                        LtiUtils.SubmitScore(slide, userId);
                    }
                }
            }
            var model = new { courseId, slideIndex = slide.Index, isLti };

            if (isLti)
            {
                return(RedirectToAction("LtiSlide", "Course", model));
            }
            return(RedirectToAction("Slide", "Course", model));
        }
Beispiel #5
0
        // Вызывается только для квизов без автопроверки
        public async Task <ActionResult> RestartQuiz(string courseId, Guid slideId, bool isLti)
        {
            var isInstructor = User.HasAccessFor(courseId, CourseRole.Instructor);
            var slide        = courseManager.GetCourse(courseId).GetSlideById(slideId, isInstructor);

            if (slide is QuizSlide)
            {
                var userId              = User.Identity.GetUserId();
                var usedAttemptsCount   = userQuizzesRepo.GetUsedAttemptsCountForQuizWithAutomaticChecking(courseId, userId, slideId);
                var maxTriesCount       = GetMaxAttemptsCount(courseId, slide as QuizSlide);
                var isQuizScoredMaximum = userQuizzesRepo.IsQuizScoredMaximum(courseId, slideId, userId);
                if (usedAttemptsCount < maxTriesCount && !isQuizScoredMaximum)
                {
                    await visitsRepo.UpdateScoreForVisit(courseId, slide, userId).ConfigureAwait(false);

                    if (isLti)
                    {
                        LtiUtils.SubmitScore(courseId, slide, userId);
                    }
                }
            }

            var model = new { courseId, slideId = slide.Id, isLti, attempt = true };

            if (isLti)
            {
                return(RedirectToAction("LtiSlide", "Course", model));
            }
            return(RedirectToAction("SlideById", "Course", model));
        }
Beispiel #6
0
        public async Task <string> SubmitQuiz(string courseId, string slideIndex, string answer, bool isLti)
        {
            var intSlideIndex = int.Parse(slideIndex);
            var course        = courseManager.GetCourse(courseId);
            var userId        = User.Identity.GetUserId();
            var slide         = course.Slides[intSlideIndex];
            var slideId       = slide.Id;

            if (visitsRepo.IsPassed(userId, slideId))
            {
                return("already answered");
            }
            var time    = DateTime.Now;
            var answers = JsonConvert.DeserializeObject <List <QuizAnswer> >(answer).GroupBy(x => x.QuizId);
            var quizBlockWithTaskCount = course.Slides[intSlideIndex].Blocks.Count(x => x is AbstractQuestionBlock);
            var incorrectQuizzes       = new List <string>();
            var fillInBlockType        = typeof(FillInBlock);
            var allQuizInfos           = new List <QuizInfoForDb>();

            foreach (var ans in answers)
            {
                var quizInfos = CreateQuizInfo(course, intSlideIndex, ans);
                foreach (var quizInfo in quizInfos)
                {
                    allQuizInfos.Add(quizInfo);
                    if (!quizInfo.IsRightAnswer && quizInfo.QuizType == fillInBlockType)
                    {
                        incorrectQuizzes.Add(ans.Key);
                    }
                }
            }
            var blocksInAnswerCount = allQuizInfos.Select(x => x.QuizId).Distinct().Count();

            if (blocksInAnswerCount != quizBlockWithTaskCount)
            {
                return("has empty blocks");
            }
            var score = allQuizInfos
                        .Where(forDb => forDb.IsRightQuizBlock)
                        .Select(forDb => forDb.QuizId)
                        .Distinct()
                        .Count();

            foreach (var quizInfoForDb in allQuizInfos)
            {
                await userQuizzesRepo.AddUserQuiz(courseId, quizInfoForDb.IsRightAnswer, quizInfoForDb.ItemId, quizInfoForDb.QuizId,
                                                  slideId, quizInfoForDb.Text, userId, time, quizInfoForDb.IsRightQuizBlock);
            }

            await visitsRepo.AddAttempt(slideId, userId, score);

            if (isLti)
            {
                LtiUtils.SubmitScore(slide, userId);
            }

            return(string.Join("*", incorrectQuizzes.Distinct()));
        }
Beispiel #7
0
        public async Task <ActionResult> RunSolution(string courseId, Guid slideId, bool isLti = false)
        {
            /* Check that no checking solution by this user in last time */
            var halfMinuteAgo = DateTime.Now.Subtract(TimeSpan.FromSeconds(30));

            if (userSolutionsRepo.IsCheckingSubmissionByUser(courseId, slideId, User.Identity.GetUserId(), halfMinuteAgo, DateTime.MaxValue))
            {
                return(Json(new RunSolutionResult
                {
                    Ignored = true,
                    ErrorMessage = "Ваше решение по этой задаче уже проверяется. Дождитесь окончания проверки"
                }));
            }

            var code = Request.InputStream.GetString();

            if (code.Length > TextsRepo.MaxTextSize)
            {
                return(Json(new RunSolutionResult
                {
                    IsCompileError = true,
                    ErrorMessage = "Слишком большой код"
                }));
            }

            var exerciseSlide = courseManager.FindCourse(courseId)?.FindSlideById(slideId) as ExerciseSlide;

            if (exerciseSlide == null)
            {
                return(HttpNotFound());
            }

            var result = await CheckSolution(
                courseId, exerciseSlide, code, User.Identity.GetUserId(), User.Identity.Name,
                waitUntilChecked : true, saveSubmissionOnCompileErrors : false
                ).ConfigureAwait(false);

            if (isLti)
            {
                try
                {
                    LtiUtils.SubmitScore(courseId, exerciseSlide, User.Identity.GetUserId());
                }
                catch (Exception e)
                {
                    ErrorLog.GetDefault(System.Web.HttpContext.Current).Log(new Error(e));
                    return(Json(new RunSolutionResult
                    {
                        IsCompillerFailure = true,
                        ErrorMessage = "Мы не смогли отправить баллы на вашу образовательную платформу. Пожалуйста, обновите страницу — мы попробуем сделать это ещё раз."
                    }));
                }
            }

            return(Json(result));
        }
Beispiel #8
0
        public async Task <ActionResult> ClearAnswers(string courseId, string slideId, bool isLti)
        {
            var slide  = courseManager.GetCourse(courseId).GetSlideById(slideId);
            var userId = User.Identity.GetUserId();
            await userQuizzesRepo.RemoveAnswers(userId, slideId);

            await visitsRepo.RemoveAttempts(slideId, userId);

            var model = new { courseId, slideIndex = slide.Index };

            if (isLti)
            {
                LtiUtils.SubmitScore(slide, userId);
                return(RedirectToAction("LtiSlide", "Course", model));
            }
            return(RedirectToAction("Slide", "Course", model));
        }
Beispiel #9
0
        public async Task ProcessResult(UserExerciseSubmission submission, RunningResults result)
        {
            var ltiRequestJson = await ltiRequestsRepo.Find(submission.CourseId, submission.UserId, submission.SlideId);

            if (ltiRequestJson != null)
            {
                try
                {
                    var exerciseSlide = (await courseManager.FindCourseAsync(submission.CourseId)).FindSlideById(submission.SlideId, true) as ExerciseSlide;
                    var score         = await visitsRepo.GetScore(submission.CourseId, submission.SlideId, submission.UserId);

                    await LtiUtils.SubmitScore(exerciseSlide, submission.UserId, score, ltiRequestJson, ltiConsumersRepo);
                }
                catch (Exception e)
                {
                    log.Error(e, "Мы не смогли отправить баллы на образовательную платформу");
                }
            }
        }
Beispiel #10
0
        public async Task <ActionResult> RunSolution(string courseId, int slideIndex = 0, bool isLti = false)
        {
            var code = Request.InputStream.GetString();

            if (code.Length > TextsRepo.MaxTextSize)
            {
                return(Json(new RunSolutionResult
                {
                    IsCompileError = true,
                    CompilationError = "Слишком большой код."
                }));
            }
            var exerciseSlide = (ExerciseSlide)courseManager.GetCourse(courseId).Slides[slideIndex];

            var result = await CheckSolution(courseId, exerciseSlide, code);

            if (isLti)
            {
                LtiUtils.SubmitScore(exerciseSlide, User.Identity.GetUserId());
            }
            return(Json(result));
        }
Beispiel #11
0
        public async Task <ActionResult> ClearAnswers(string courseId, Guid slideId, bool isLti)
        {
            var slide = courseManager.FindCourse(courseId)?.FindSlideById(slideId) as QuizSlide;

            if (slide == null)
            {
                return(HttpNotFound());
            }

            var userId = User.Identity.GetUserId();
            await userQuizzesRepo.RemoveAnswers(userId, slideId);

            await visitsRepo.RemoveAttempts(slideId, userId);

            var model = new { courseId, slideId = slide.Id };

            if (isLti)
            {
                LtiUtils.SubmitScore(slide, userId);
                return(RedirectToAction("LtiSlide", "Course", model));
            }
            return(RedirectToAction("SlideById", "Course", model));
        }
Beispiel #12
0
        public async Task <ActionResult> SubmitQuiz(string courseId, Guid slideId, string answer, bool isLti)
        {
            metricSender.SendCount("quiz.submit");
            if (isLti)
            {
                metricSender.SendCount("quiz.submit.lti");
            }
            metricSender.SendCount($"quiz.submit.{courseId}");
            metricSender.SendCount($"quiz.submit.{courseId}.{slideId}");

            var course = courseManager.GetCourse(courseId);
            var slide  = course.FindSlideById(slideId) as QuizSlide;

            if (slide == null)
            {
                return(new HttpNotFoundResult());
            }

            var userId        = User.Identity.GetUserId();
            var maxTriesCount = GetMaxTriesCount(courseId, slide);
            var quizState     = GetQuizState(courseId, userId, slideId);

            if (!CanUserFillQuiz(quizState.Item1))
            {
                return(new HttpStatusCodeResult(HttpStatusCode.OK, "Already answered"));
            }

            var tryIndex = quizState.Item2;

            metricSender.SendCount($"quiz.submit.try.{tryIndex}");
            metricSender.SendCount($"quiz.submit.{courseId}.try.{tryIndex}");
            metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.try.{tryIndex}");

            if (slide.ManualChecking && !groupsRepo.IsManualCheckingEnabledForUser(course, userId))
            {
                return(new HttpStatusCodeResult(HttpStatusCode.OK, "Manual checking is disabled for you"));
            }

            var time    = DateTime.Now;
            var answers = JsonConvert.DeserializeObject <List <QuizAnswer> >(answer).GroupBy(x => x.QuizId);
            var quizBlockWithTaskCount = slide.Blocks.Count(x => x is AbstractQuestionBlock);
            var allQuizInfos           = new List <QuizInfoForDb>();

            foreach (var ans in answers)
            {
                var quizInfos = CreateQuizInfo(slide, ans);
                if (quizInfos != null)
                {
                    allQuizInfos.AddRange(quizInfos);
                }
            }
            var blocksInAnswerCount = allQuizInfos.Select(x => x.QuizId).Distinct().Count();

            if (blocksInAnswerCount != quizBlockWithTaskCount)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.Forbidden, "Has empty blocks"));
            }

            using (var transaction = db.Database.BeginTransaction())
            {
                await userQuizzesRepo.RemoveUserQuizzes(courseId, slideId, userId);

                foreach (var quizInfoForDb in allQuizInfos)
                {
                    await userQuizzesRepo.AddUserQuiz(courseId, quizInfoForDb.IsRightAnswer, quizInfoForDb.ItemId, quizInfoForDb.QuizId,
                                                      slideId, quizInfoForDb.Text, userId, time, quizInfoForDb.QuizBlockScore, quizInfoForDb.QuizBlockMaxScore);
                }

                transaction.Commit();
            }

            if (slide.ManualChecking)
            {
                /* If this quiz is already queued for checking for this user, don't add it to queue again */
                if (quizState.Item1 != QuizState.WaitForCheck)
                {
                    await slideCheckingsRepo.AddQuizAttemptForManualChecking(courseId, slideId, userId);

                    await visitsRepo.MarkVisitsAsWithManualChecking(courseId, slideId, userId);
                }
            }
            /* Recalculate score for quiz if this attempt is allowed. Don't recalculate score if this attempt is more then maxTriesCount */
            else if (tryIndex < maxTriesCount)
            {
                var score = allQuizInfos
                            .DistinctBy(forDb => forDb.QuizId)
                            .Sum(forDb => forDb.QuizBlockScore);

                metricSender.SendCount($"quiz.submit.try.{tryIndex}.score", score);
                metricSender.SendCount($"quiz.submit.{courseId}.try.{tryIndex}.score", score);
                metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.try.{tryIndex}.score", score);
                metricSender.SendCount($"quiz.submit.score", score);
                metricSender.SendCount($"quiz.submit.{courseId}.score", score);
                metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.score", score);

                if (score == slide.MaxScore)
                {
                    metricSender.SendCount($"quiz.submit.try.{tryIndex}.full_passed");
                    metricSender.SendCount($"quiz.submit.{courseId}.try.{tryIndex}.full_passed");
                    metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.try.{tryIndex}.full_passed");
                    metricSender.SendCount($"quiz.submit.full_passed");
                    metricSender.SendCount($"quiz.submit.{courseId}.full_passed");
                    metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.full_passed");
                }

                await slideCheckingsRepo.AddQuizAttemptWithAutomaticChecking(courseId, slideId, userId, score);

                await visitsRepo.UpdateScoreForVisit(courseId, slideId, userId);

                if (isLti)
                {
                    LtiUtils.SubmitScore(courseId, slide, userId);
                }
            }

            return(Json(new
            {
                url = Url.RouteUrl("Course.SlideById", new { courseId = courseId, slideId = slide.Url, send = 1 })
            }));
        }
Beispiel #13
0
        public async Task <ActionResult> LtiSlide(string courseId, Guid slideId)
        {
            if (string.IsNullOrWhiteSpace(courseId))
            {
                return(RedirectToAction("Index", "Home"));
            }

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

            string userId;
            var    owinRequest = Request.GetOwinContext().Request;

            if (await owinRequest.IsAuthenticatedLtiRequestAsync())
            {
                var ltiRequest = await owinRequest.ParseLtiRequestAsync();

                log.Info($"Нашёл LTI request в запросе: {ltiRequest.JsonSerialize()}");
                userId = Request.GetOwinContext().Authentication.AuthenticationResponseGrant.Identity.GetUserId();
                await ltiRequestsRepo.Update(courseId, userId, slide.Id, ltiRequest.JsonSerialize());

                /* Substitute http(s) scheme with real scheme from header */
                var uriBuilder = new UriBuilder(ltiRequest.Url)
                {
                    Scheme = owinRequest.GetRealRequestScheme(),
                    Port   = owinRequest.GetRealRequestPort()
                };
                return(Redirect(uriBuilder.Uri.AbsoluteUri));
            }

            /* For now user should be authenticated */
            if (!User.Identity.IsAuthenticated)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.Forbidden));
            }

            userId = User.Identity.GetUserId();
            var visit = await VisitSlide(courseId, slide.Id, userId);

            /* Try to send score via LTI immediately after slide visiting */
            try
            {
                if (visit.IsPassed)
                {
                    LtiUtils.SubmitScore(courseId, slide, userId, visit);
                }
            }
            catch (Exception e)
            {
                ErrorLog.GetDefault(System.Web.HttpContext.Current).Log(new Error(e));
            }

            var exerciseSlide = slide as ExerciseSlide;

            if (exerciseSlide != null)
            {
                var model = new CodeModel
                {
                    CourseId      = courseId,
                    SlideId       = exerciseSlide.Id,
                    ExerciseBlock = exerciseSlide.Exercise,
                    Context       = CreateRenderContext(course, exerciseSlide, isLti: true)
                };
                return(View("LtiExerciseSlide", model));
            }

            var quizSlide = slide as QuizSlide;

            if (quizSlide != null)
            {
                var model = new LtiQuizModel
                {
                    CourseId = courseId,
                    Slide    = quizSlide,
                    UserId   = userId
                };
                return(View("LtiQuizSlide", model));
            }

            return(View());
        }
Beispiel #14
0
        public async Task <ActionResult <RunSolutionResponse> > RunSolution(
            [FromRoute] Course course,
            [FromRoute] Guid slideId,
            [FromBody] RunSolutionParameters parameters,
            [FromQuery] Language language,
            [FromQuery] bool isLti = false)
        {
            var courseId = course.Id;

            /* Check that no checking solution by this user in last time */
            var delta         = TimeSpan.FromSeconds(30);
            var halfMinuteAgo = DateTime.Now.Subtract(delta);

            if (await userSolutionsRepo.IsCheckingSubmissionByUser(courseId, slideId, User.Identity.GetUserId(), halfMinuteAgo, DateTime.MaxValue))
            {
                return(Json(new RunSolutionResponse(SolutionRunStatus.Ignored)
                {
                    Message = $"Ваше решение по этой задаче уже проверяется. Дождитесь окончания проверки. Вы можете отправить новое решение через {delta.Seconds} секунд."
                }));
            }

            var code = parameters.Solution;

            if (code.Length > TextsRepo.MaxTextSize)
            {
                return(Json(new RunSolutionResponse(SolutionRunStatus.Ignored)
                {
                    Message = "Слишком длинный код"
                }));
            }

            var isInstructor = await courseRolesRepo.HasUserAccessToCourseAsync(UserId, courseId, CourseRoleType.Instructor);

            var exerciseSlide = (await courseManager.FindCourseAsync(courseId))?.FindSlideById(slideId, isInstructor) as ExerciseSlide;

            if (exerciseSlide == null)
            {
                return(NotFound(new ErrorResponse("Slide not found")));
            }

            var result = await CheckSolution(
                courseId, exerciseSlide, code, language, User.Identity.GetUserId(), User.Identity.Name,
                waitUntilChecked : true, saveSubmissionOnCompileErrors : false
                ).ConfigureAwait(false);

            if (isLti)
            {
                try
                {
                    var score = await visitsRepo.GetScore(courseId, slideId, UserId);

                    await LtiUtils.SubmitScore(courseId, exerciseSlide, User.Identity.GetUserId(), score, ltiRequestsRepo, ltiConsumersRepo);
                }
                catch (Exception e)
                {
                    log.Error(e, "Мы не смогли отправить баллы на вашу образовательную платформу");
                    return(Json(new RunSolutionResponse(SolutionRunStatus.InternalServerError)
                    {
                        Message = "Мы не смогли отправить баллы на вашу образовательную платформу. Пожалуйста, обновите страницу — мы попробуем сделать это ещё раз."
                    }));
                }
            }

            return(result);
        }
Beispiel #15
0
        public async Task <ActionResult> SubmitQuiz(string courseId, Guid slideId, string answer, bool isLti)
        {
            var course = courseManager.GetCourse(courseId);
            var slide  = course.FindSlideById(slideId) as QuizSlide;

            if (slide == null)
            {
                return(new HttpNotFoundResult());
            }

            var userId       = User.Identity.GetUserId();
            var maxDropCount = GetMaxDropCount(slide);
            var quizState    = GetQuizState(courseId, userId, slideId, maxDropCount).Item1;

            if (!CanUserFillQuiz(quizState))
            {
                return(new HttpStatusCodeResult(HttpStatusCode.OK, "Already answered"));
            }

            if (slide.ManualChecking && !groupsRepo.IsManualCheckingEnabledForUser(course, userId))
            {
                return(new HttpStatusCodeResult(HttpStatusCode.OK, "Manual checking is disabled for you"));
            }

            var time    = DateTime.Now;
            var answers = JsonConvert.DeserializeObject <List <QuizAnswer> >(answer).GroupBy(x => x.QuizId);
            var quizBlockWithTaskCount = slide.Blocks.Count(x => x is AbstractQuestionBlock);
            var allQuizInfos           = new List <QuizInfoForDb>();

            foreach (var ans in answers)
            {
                var quizInfos = CreateQuizInfo(slide, ans);
                if (quizInfos != null)
                {
                    allQuizInfos.AddRange(quizInfos);
                }
            }
            var blocksInAnswerCount = allQuizInfos.Select(x => x.QuizId).Distinct().Count();

            if (blocksInAnswerCount != quizBlockWithTaskCount)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.Forbidden, "Has empty blocks"));
            }

            using (var transaction = db.Database.BeginTransaction())
            {
                await userQuizzesRepo.RemoveUserQuizzes(courseId, slideId, userId);

                foreach (var quizInfoForDb in allQuizInfos)
                {
                    await userQuizzesRepo.AddUserQuiz(courseId, quizInfoForDb.IsRightAnswer, quizInfoForDb.ItemId, quizInfoForDb.QuizId,
                                                      slideId, quizInfoForDb.Text, userId, time, quizInfoForDb.QuizBlockScore, quizInfoForDb.QuizBlockMaxScore);
                }

                transaction.Commit();
            }

            if (slide.ManualChecking)
            {
                /* If this quiz is already queued for checking for this user, don't add it to queue again */
                if (quizState != QuizState.WaitForCheck)
                {
                    await slideCheckingsRepo.AddQuizAttemptForManualChecking(courseId, slideId, userId);

                    await visitsRepo.MarkVisitsAsWithManualChecking(slideId, userId);
                }
            }
            else
            {
                var score = allQuizInfos
                            .DistinctBy(forDb => forDb.QuizId)
                            .Sum(forDb => forDb.QuizBlockScore);
                await slideCheckingsRepo.AddQuizAttemptWithAutomaticChecking(courseId, slideId, userId, score);

                await visitsRepo.UpdateScoreForVisit(courseId, slideId, userId);

                if (isLti)
                {
                    LtiUtils.SubmitScore(slide, userId);
                }
            }

            return(new HttpStatusCodeResult(HttpStatusCode.OK));
        }
Beispiel #16
0
        public async Task <ActionResult> SubmitQuiz([FromBody] SubmitQuizRequest request)
        {
            var isLti    = request.IsLti;
            var courseId = request.CourseId;
            var slideId  = request.SlideId;
            var answer   = request.Answer;

            metricSender.SendCount("quiz.submit");
            if (isLti)
            {
                metricSender.SendCount("quiz.submit.lti");
            }
            metricSender.SendCount($"quiz.submit.{courseId}");
            metricSender.SendCount($"quiz.submit.{courseId}.{slideId}");

            var course       = courseManager.GetCourse(courseId);
            var isInstructor = User.HasAccessFor(courseId, CourseRole.Instructor);
            var slide        = course.FindSlideById(slideId, isInstructor) as QuizSlide;

            if (slide == null)
            {
                return(new HttpNotFoundResult());
            }

            var userId        = User.Identity.GetUserId();
            var maxTriesCount = GetMaxAttemptsCount(courseId, slide);

            /* Not it's not important what user's score is, so just pass 0 */
            var state = GetQuizState(courseId, userId, slideId, 0, slide.MaxScore);

            if (!CanUserFillQuiz(state.Status))
            {
                return(new HttpStatusCodeResult(HttpStatusCode.OK, "Already answered"));
            }

            var attemptNumber = state.UsedAttemptsCount;

            metricSender.SendCount($"quiz.submit.try.{attemptNumber}");
            metricSender.SendCount($"quiz.submit.{courseId}.try.{attemptNumber}");
            metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.try.{attemptNumber}");

            if (slide.ManualChecking && !groupsRepo.IsManualCheckingEnabledForUser(course, userId))
            {
                return(new HttpStatusCodeResult(HttpStatusCode.OK, "Manual checking is disabled for you"));
            }

            var answers = JsonConvert.DeserializeObject <List <QuizAnswer> >(answer).GroupBy(x => x.BlockId);
            var quizBlockWithTaskCount = slide.Blocks.Count(x => x is AbstractQuestionBlock);
            var allQuizInfos           = new List <QuizInfoForDb>();

            foreach (var ans in answers)
            {
                var quizInfos = CreateQuizInfo(slide, ans);
                if (quizInfos != null)
                {
                    allQuizInfos.AddRange(quizInfos);
                }
            }

            var blocksInAnswerCount = allQuizInfos.Select(x => x.BlockId).Distinct().Count();

            if (blocksInAnswerCount != quizBlockWithTaskCount)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.Forbidden, "Has empty blocks"));
            }

            UserQuizSubmission submission;

            using (var transaction = db.Database.BeginTransaction())
            {
                submission = await userQuizzesRepo.AddSubmission(courseId, slideId, userId, DateTime.Now).ConfigureAwait(false);

                foreach (var quizInfoForDb in allQuizInfos)
                {
                    await userQuizzesRepo.AddUserQuizAnswer(
                        submission.Id,
                        quizInfoForDb.IsRightAnswer,
                        quizInfoForDb.BlockId,
                        quizInfoForDb.ItemId,
                        quizInfoForDb.Text,
                        quizInfoForDb.QuizBlockScore,
                        quizInfoForDb.QuizBlockMaxScore
                        ).ConfigureAwait(false);
                }

                transaction.Commit();
            }

            if (slide.ManualChecking)
            {
                /* If this quiz is already queued for checking for this user, remove waiting checkings */
                if (state.Status == QuizStatus.WaitsForManualChecking)
                {
                    await slideCheckingsRepo.RemoveWaitingManualCheckings <ManualQuizChecking>(courseId, slideId, userId).ConfigureAwait(false);
                }

                await slideCheckingsRepo.AddManualQuizChecking(submission, courseId, slideId, userId).ConfigureAwait(false);

                await visitsRepo.MarkVisitsAsWithManualChecking(courseId, slideId, userId).ConfigureAwait(false);
            }
            /* Recalculate score for quiz if this attempt is allowed. Don't recalculate score if this attempt number is more then maxTriesCount */
            else if (attemptNumber < maxTriesCount)
            {
                var score = allQuizInfos
                            .DistinctBy(forDb => forDb.BlockId)
                            .Sum(forDb => forDb.QuizBlockScore);

                metricSender.SendCount($"quiz.submit.try.{attemptNumber}.score", score);
                metricSender.SendCount($"quiz.submit.{courseId}.try.{attemptNumber}.score", score);
                metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.try.{attemptNumber}.score", score);
                metricSender.SendCount($"quiz.submit.score", score);
                metricSender.SendCount($"quiz.submit.{courseId}.score", score);
                metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.score", score);

                if (score == slide.MaxScore)
                {
                    metricSender.SendCount($"quiz.submit.try.{attemptNumber}.full_passed");
                    metricSender.SendCount($"quiz.submit.{courseId}.try.{attemptNumber}.full_passed");
                    metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.try.{attemptNumber}.full_passed");
                    metricSender.SendCount($"quiz.submit.full_passed");
                    metricSender.SendCount($"quiz.submit.{courseId}.full_passed");
                    metricSender.SendCount($"quiz.submit.{courseId}.{slideId}.full_passed");
                }

                await slideCheckingsRepo.AddAutomaticQuizChecking(submission, courseId, slideId, userId, score).ConfigureAwait(false);

                await visitsRepo.UpdateScoreForVisit(courseId, slide, userId).ConfigureAwait(false);

                if (isLti)
                {
                    LtiUtils.SubmitScore(courseId, slide, userId);
                }
            }

            return(Json(new
            {
                url = isLti
                                        ? Url.Action("LtiSlide", "Course", new { courseId = courseId, slideId = slide.Id, send = 1 })
                                        : Url.RouteUrl("Course.SlideById", new { courseId = courseId, slideId = slide.Url, send = 1 })
            }));
        }