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)); }
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); } } }
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)); }
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)); }
// Вызывается только для квизов без автопроверки 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)); }
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())); }
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)); }
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)); }
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, "Мы не смогли отправить баллы на образовательную платформу"); } } }
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)); }
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)); }
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 }) })); }
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()); }
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); }
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)); }
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 }) })); }