private async Task AddManualCheckingsForOldSolutionsAsync(string courseId, string userId) { logger.Information($"Создаю ручные проверки для всех решения пользователя {userId} в курсе {courseId}"); var course = courseManager.GetCourse(courseId); /* For exercises */ var acceptedSubmissionsBySlide = userSolutionsRepo.GetAllAcceptedSubmissionsByUser(courseId, userId) .GroupBy(s => s.SlideId) .ToDictionary(g => g.Key, g => g.ToList()); foreach (var acceptedSubmissionsForSlide in acceptedSubmissionsBySlide.Values) { /* If exists at least one manual checking for at least one submissions on slide, then ignore this slide */ if (!acceptedSubmissionsForSlide.Any(s => s.ManualCheckings.Any())) { /* Otherwise found the latest accepted submission */ var lastSubmission = acceptedSubmissionsForSlide.OrderByDescending(s => s.Timestamp).First(); var slideId = lastSubmission.SlideId; var slide = course.FindSlideById(slideId) as ExerciseSlide; if (slide == null || !slide.Exercise.RequireReview) { continue; } logger.Information($"Создаю ручную проверку для решения {lastSubmission.Id}, слайд {slideId}"); await slideCheckingsRepo.AddManualExerciseChecking(courseId, slideId, userId, lastSubmission).ConfigureAwait(false); await visitsRepo.MarkVisitsAsWithManualChecking(slideId, userId).ConfigureAwait(false); } } /* For quizzes */ var passedQuizzesIds = userQuizzesRepo.GetIdOfQuizPassedSlides(courseId, userId); foreach (var quizSlideId in passedQuizzesIds) { var slide = course.FindSlideById(quizSlideId) as QuizSlide; if (slide == null || !slide.ManualChecking) { continue; } if (!userQuizzesRepo.IsWaitingForManualCheck(courseId, quizSlideId, userId)) { logger.Information($"Создаю ручную проверку для теста {slide.Id}"); await slideCheckingsRepo.AddQuizAttemptForManualChecking(courseId, quizSlideId, userId).ConfigureAwait(false); await visitsRepo.MarkVisitsAsWithManualChecking(quizSlideId, userId).ConfigureAwait(false); } } }
public static async Task <bool> SendToReviewAndUpdateScore(UserExerciseSubmission submission, CourseManager courseManager, SlideCheckingsRepo slideCheckingsRepo, GroupsRepo groupsRepo, VisitsRepo visitsRepo, MetricSender metricSender, bool startTransaction) { var userId = submission.User.Id; var courseId = submission.CourseId; var course = courseManager.GetCourse(courseId); var exerciseSlide = course.FindSlideById(submission.SlideId, true) as ExerciseSlide; // SlideId проверен в вызывающем методе if (exerciseSlide == null) { return(false); } var exerciseMetricId = GetExerciseMetricId(courseId, exerciseSlide); var automaticChecking = submission.AutomaticChecking; var isProhibitedUserToSendForReview = slideCheckingsRepo.IsProhibitedToSendExerciseToManualChecking(courseId, exerciseSlide.Id, userId); var sendToReview = exerciseSlide.Scoring.RequireReview && submission.AutomaticCheckingIsRightAnswer && !isProhibitedUserToSendForReview && groupsRepo.IsManualCheckingEnabledForUser(course, userId); if (sendToReview) { await slideCheckingsRepo.RemoveWaitingManualCheckings <ManualExerciseChecking>(courseId, exerciseSlide.Id, userId, false); await slideCheckingsRepo.AddManualExerciseChecking(courseId, exerciseSlide.Id, userId, submission); await visitsRepo.MarkVisitsAsWithManualChecking(courseId, exerciseSlide.Id, userId); metricSender.SendCount($"exercise.{exerciseMetricId}.sent_to_review"); metricSender.SendCount("exercise.sent_to_review"); } await visitsRepo.UpdateScoreForVisit(courseId, exerciseSlide, userId); if (automaticChecking != null) { var verdictForMetric = automaticChecking.GetVerdict().Replace(" ", ""); metricSender.SendCount($"exercise.{exerciseMetricId}.{verdictForMetric}"); } return(sendToReview); }
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> 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)); }
protected async Task <RunSolutionResult> CheckSolution(string courseId, ExerciseSlide exerciseSlide, string userCode, string userId, string userName, bool waitUntilChecked, bool saveSubmissionOnCompileErrors) { var slideTitleForMetric = exerciseSlide.LatinTitle.Replace(".", "_").ToLower(CultureInfo.InvariantCulture); if (slideTitleForMetric.Length > 25) { slideTitleForMetric = slideTitleForMetric.Substring(0, 25); } var exerciseMetricId = $"{courseId.ToLower(CultureInfo.InvariantCulture)}.{exerciseSlide.Id.ToString("N").Substring(32 - 25)}.{slideTitleForMetric}"; metricSender.SendCount("exercise.try"); metricSender.SendCount($"exercise.{courseId.ToLower(CultureInfo.InvariantCulture)}.try"); metricSender.SendCount($"exercise.{exerciseMetricId}.try"); var course = courseManager.GetCourse(courseId); var exerciseBlock = exerciseSlide.Exercise; var buildResult = exerciseBlock.BuildSolution(userCode); if (buildResult.HasErrors) { metricSender.SendCount($"exercise.{exerciseMetricId}.CompilationError"); } if (buildResult.HasStyleErrors) { metricSender.SendCount($"exercise.{exerciseMetricId}.StyleViolation"); } if (!saveSubmissionOnCompileErrors) { if (buildResult.HasErrors) { return new RunSolutionResult { IsCompileError = true, ErrorMessage = buildResult.ErrorMessage, ExecutionServiceName = "uLearn" } } ; } var compilationErrorMessage = buildResult.HasErrors ? buildResult.ErrorMessage : null; var dontRunSubmission = buildResult.HasErrors; var submissionLanguage = SubmissionLanguageHelpers.ByLangId(exerciseSlide.Exercise.LangId); var submission = await userSolutionsRepo.AddUserExerciseSubmission( courseId, exerciseSlide.Id, userCode, compilationErrorMessage, null, userId, "uLearn", GenerateSubmissionName(exerciseSlide, userName), submissionLanguage, dontRunSubmission?AutomaticExerciseCheckingStatus.Done : AutomaticExerciseCheckingStatus.Waiting ); if (buildResult.HasErrors) { return new RunSolutionResult { IsCompileError = true, ErrorMessage = buildResult.ErrorMessage, SubmissionId = submission.Id, ExecutionServiceName = "uLearn" } } ; try { if (submissionLanguage.HasAutomaticChecking()) { await userSolutionsRepo.RunAutomaticChecking(submission, executionTimeout, waitUntilChecked); } } catch (SubmissionCheckingTimeout) { log.Error($"Не смог запустить проверку решения, никто не взял его на проверку за {executionTimeout.TotalSeconds} секунд.\nКурс «{course.Title}», слайд «{exerciseSlide.Title}» ({exerciseSlide.Id})"); errorsBot.PostToChannel($"Не смог запустить проверку решения, никто не взял его на проверку за {executionTimeout.TotalSeconds} секунд.\nКурс «{course.Title}», слайд «{exerciseSlide.Title}» ({exerciseSlide.Id})\n\nhttps://ulearn.me/Sandbox"); return(new RunSolutionResult { IsCompillerFailure = true, ErrorMessage = "К сожалению, из-за большой нагрузки мы не смогли оперативно проверить ваше решение. " + "Мы попробуем проверить его позже, просто подождите и обновите страницу. ", ExecutionServiceName = "uLearn" }); } if (!waitUntilChecked) { metricSender.SendCount($"exercise.{exerciseMetricId}.dont_wait_result"); return(new RunSolutionResult { SubmissionId = submission.Id }); } /* Update the submission */ submission = userSolutionsRepo.FindNoTrackingSubmission(submission.Id); var automaticChecking = submission.AutomaticChecking; var isProhibitedUserToSendForReview = slideCheckingsRepo.IsProhibitedToSendExerciseToManualChecking(courseId, exerciseSlide.Id, userId); var sendToReview = exerciseBlock.RequireReview && submission.AutomaticCheckingIsRightAnswer && !isProhibitedUserToSendForReview && groupsRepo.IsManualCheckingEnabledForUser(course, userId); if (sendToReview) { await slideCheckingsRepo.RemoveWaitingManualExerciseCheckings(courseId, exerciseSlide.Id, userId); await slideCheckingsRepo.AddManualExerciseChecking(courseId, exerciseSlide.Id, userId, submission); await visitsRepo.MarkVisitsAsWithManualChecking(courseId, exerciseSlide.Id, userId); metricSender.SendCount($"exercise.{exerciseMetricId}.sent_to_review"); metricSender.SendCount("exercise.sent_to_review"); } await visitsRepo.UpdateScoreForVisit(courseId, exerciseSlide.Id, userId); if (automaticChecking != null) { var verdictForMetric = automaticChecking.GetVerdict().Replace(" ", ""); metricSender.SendCount($"exercise.{exerciseMetricId}.{verdictForMetric}"); } if (submission.AutomaticCheckingIsRightAnswer) { await CreateStyleErrorsReviewsForSubmission(submission, buildResult.StyleErrors, exerciseMetricId); } var result = new RunSolutionResult { IsCompileError = automaticChecking?.IsCompilationError ?? false, ErrorMessage = automaticChecking?.CompilationError.Text ?? "", IsRightAnswer = submission.AutomaticCheckingIsRightAnswer, ExpectedOutput = exerciseBlock.HideExpectedOutputOnError ? null : exerciseSlide.Exercise.ExpectedOutput.NormalizeEoln(), ActualOutput = automaticChecking?.Output.Text ?? "", ExecutionServiceName = automaticChecking?.ExecutionServiceName ?? "ulearn", SentToReview = sendToReview, SubmissionId = submission.Id, }; if (buildResult.HasStyleErrors) { result.IsStyleViolation = true; result.StyleMessage = string.Join("\n", buildResult.StyleErrors.Select(e => e.GetMessageWithPositions())); } return(result); }
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 = 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, slideId, slide.MaxScore, userId).ConfigureAwait(false); if (isLti) { LtiUtils.SubmitScore(courseId, slide, userId); } } return(Json(new { url = Url.RouteUrl("Course.SlideById", new { courseId = courseId, slideId = slide.Url, send = 1 }) })); }
private async Task <RunSolutionResult> CheckSolution(string courseId, ExerciseSlide exerciseSlide, string userCode) { var exerciseBlock = exerciseSlide.Exercise; var userId = User.Identity.GetUserId(); var solution = exerciseBlock.BuildSolution(userCode); if (solution.HasErrors) { return new RunSolutionResult { IsCompileError = true, ErrorMessage = solution.ErrorMessage, ExecutionServiceName = "uLearn" } } ; if (solution.HasStyleIssues) { return new RunSolutionResult { IsStyleViolation = true, ErrorMessage = solution.StyleMessage, ExecutionServiceName = "uLearn" } } ; var submission = await solutionsRepo.RunUserSolution( courseId, exerciseSlide.Id, userId, userCode, null, null, false, "uLearn", GenerateSubmissionName(exerciseSlide), executionTimeout ); var course = courseManager.GetCourse(courseId); if (submission == null) { log.Error($"Не смог запустить проверку решения, никто не взял его на проверку за {executionTimeout.TotalSeconds} секунд.\nКурс «{course.Title}», слайд «{exerciseSlide.Title}» ({exerciseSlide.Id})"); errorsBot.PostToChannel($"Не смог запустить проверку решения, никто не взял его на проверку за {executionTimeout.TotalSeconds} секунд.\nКурс «{course.Title}», слайд «{exerciseSlide.Title}» ({exerciseSlide.Id})\n\nhttps://ulearn.me/Sandbox"); return(new RunSolutionResult { IsCompillerFailure = true, ErrorMessage = "К сожалению, из-за большой нагрузки мы не смогли оперативно проверить ваше решение. " + "Мы попробуем проверить его позже, просто подождите и обновите страницу. ", ExecutionServiceName = "uLearn" }); } var automaticChecking = submission.AutomaticChecking; var isProhibitedUserToSendForReview = slideCheckingsRepo.IsProhibitedToSendExerciseToManualChecking(courseId, exerciseSlide.Id, userId); var sendToReview = exerciseBlock.RequireReview && automaticChecking.IsRightAnswer && !isProhibitedUserToSendForReview && groupsRepo.IsManualCheckingEnabledForUser(course, userId); if (sendToReview) { await slideCheckingsRepo.RemoveWaitingManualExerciseCheckings(courseId, exerciseSlide.Id, userId); await slideCheckingsRepo.AddManualExerciseChecking(courseId, exerciseSlide.Id, userId, submission); await visitsRepo.MarkVisitsAsWithManualChecking(exerciseSlide.Id, userId); } await visitsRepo.UpdateScoreForVisit(courseId, exerciseSlide.Id, userId); return(new RunSolutionResult { IsCompileError = automaticChecking.IsCompilationError, ErrorMessage = automaticChecking.CompilationError.Text, IsRightAnswer = automaticChecking.IsRightAnswer, ExpectedOutput = exerciseBlock.HideExpectedOutputOnError ? null : exerciseSlide.Exercise.ExpectedOutput.NormalizeEoln(), ActualOutput = automaticChecking.Output.Text, ExecutionServiceName = automaticChecking.ExecutionServiceName, SentToReview = sendToReview, SubmissionId = submission.Id, }); }