private async Task <RunSolutionResponse> CheckSolution(string courseId, ExerciseSlide exerciseSlide, string userCode, Language language, string userId, string userName, bool waitUntilChecked, bool saveSubmissionOnCompileErrors ) { var exerciseMetricId = GetExerciseMetricId(courseId, exerciseSlide); metricSender.SendCount("exercise.try"); metricSender.SendCount($"exercise.{courseId.ToLower(CultureInfo.InvariantCulture)}.try"); metricSender.SendCount($"exercise.{exerciseMetricId}.try"); var course = await courseManager.GetCourseAsync(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 RunSolutionResponse(SolutionRunStatus.CompilationError) { Message = buildResult.ErrorMessage } } ; } var compilationErrorMessage = buildResult.HasErrors ? buildResult.ErrorMessage : null; var submissionSandbox = (exerciseBlock as UniversalExerciseBlock)?.DockerImageName; var hasAutomaticChecking = exerciseBlock.HasAutomaticChecking(); var automaticCheckingStatus = hasAutomaticChecking ? buildResult.HasErrors ? AutomaticExerciseCheckingStatus.Done : AutomaticExerciseCheckingStatus.Waiting : (AutomaticExerciseCheckingStatus?)null; var initialSubmission = await userSolutionsRepo.AddUserExerciseSubmission( courseId, exerciseSlide.Id, userCode, compilationErrorMessage, null, userId, "uLearn", GenerateSubmissionName(exerciseSlide, userName), language, submissionSandbox, hasAutomaticChecking, automaticCheckingStatus ); var isCourseAdmin = await courseRolesRepo.HasUserAccessToCourseAsync(userId, courseId, CourseRoleType.CourseAdmin); if (buildResult.HasErrors) { return new RunSolutionResponse(SolutionRunStatus.Success) { Submission = SubmissionInfo.Build(initialSubmission, null, isCourseAdmin) } } ; var executionTimeout = TimeSpan.FromSeconds(exerciseBlock.TimeLimit * 2 + 5); UserExerciseSubmission updatedSubmissionNoTracking; try { if (hasAutomaticChecking) { var priority = exerciseBlock is SingleFileExerciseBlock ? 10 : 0; await userSolutionsRepo.RunAutomaticChecking(initialSubmission, executionTimeout, waitUntilChecked, priority); } } catch (SubmissionCheckingTimeout) { log.Error($"Не смог запустить проверку решения, никто не взял его на проверку за {executionTimeout.TotalSeconds} секунд.\nКурс «{course.Title}», слайд «{exerciseSlide.Title}» ({exerciseSlide.Id})"); await errorsBot.PostToChannelAsync($"Не смог запустить проверку решения, никто не взял его на проверку за {executionTimeout.TotalSeconds} секунд.\nКурс «{course.Title}», слайд «{exerciseSlide.Title}» ({exerciseSlide.Id})\n\nhttps://ulearn.me/Sandbox"); updatedSubmissionNoTracking = await userSolutionsRepo.FindSubmissionByIdNoTracking(initialSubmission.Id); var message = updatedSubmissionNoTracking.AutomaticChecking.Status == AutomaticExerciseCheckingStatus.Running ? "Решение уже проверяется." : "Решение ждет своей очереди на проверку, мы будем пытаться проверить его еще 10 минут."; return(new RunSolutionResponse(SolutionRunStatus.SubmissionCheckingTimeout) { Message = $"К сожалению, мы не смогли оперативно проверить ваше решение. {message}. Просто подождите и обновите страницу.", Submission = SubmissionInfo.Build(updatedSubmissionNoTracking, null, isCourseAdmin) }); } if (!waitUntilChecked) { metricSender.SendCount($"exercise.{exerciseMetricId}.dont_wait_result"); // По вовзращаемому значению нельзя отличить от случая, когда никто не взял на проверку return(new RunSolutionResponse(SolutionRunStatus.Success) { Submission = SubmissionInfo.Build(initialSubmission, null, isCourseAdmin) }); } updatedSubmissionNoTracking = await userSolutionsRepo.FindSubmissionByIdNoTracking(initialSubmission.Id); updatedSubmissionNoTracking.Reviews = await CreateStyleErrorsReviewsForSubmission(updatedSubmissionNoTracking.Id, buildResult.StyleErrors, exerciseMetricId); if (!hasAutomaticChecking) { await SendToReviewAndUpdateScore(updatedSubmissionNoTracking, courseManager, slideCheckingsRepo, groupsRepo, visitsRepo, metricSender); } var score = await visitsRepo.GetScore(courseId, exerciseSlide.Id, userId); var waitingForManualChecking = updatedSubmissionNoTracking.ManualCheckings.Any(c => !c.IsChecked) ? true : (bool?)null; var prohibitFurtherManualChecking = updatedSubmissionNoTracking.ManualCheckings.Any(c => c.ProhibitFurtherManualCheckings); var result = new RunSolutionResponse(SolutionRunStatus.Success) { Score = score, WaitingForManualChecking = waitingForManualChecking, ProhibitFurtherManualChecking = prohibitFurtherManualChecking, Submission = SubmissionInfo.Build(updatedSubmissionNoTracking, null, isCourseAdmin) }; return(result); }
private async Task <RunSolutionResponse> CheckSolution(string courseId, ExerciseSlide exerciseSlide, string userCode, Language language, string userId, string userName, bool waitUntilChecked, bool saveSubmissionOnCompileErrors ) { var exerciseMetricId = RunnerSetResultController.GetExerciseMetricId(courseId, exerciseSlide); metricSender.SendCount("exercise.try"); metricSender.SendCount($"exercise.{courseId.ToLower(CultureInfo.InvariantCulture)}.try"); metricSender.SendCount($"exercise.{exerciseMetricId}.try"); var course = await courseManager.GetCourseAsync(courseId); var exerciseBlock = exerciseSlide.Exercise; var buildResult = exerciseBlock.BuildSolution(userCode); if (buildResult.HasErrors) { metricSender.SendCount($"exercise.{exerciseMetricId}.CompilationError"); } if (!saveSubmissionOnCompileErrors) { if (buildResult.HasErrors) { return new RunSolutionResponse(SolutionRunStatus.CompilationError) { Message = buildResult.ErrorMessage } } ; } var hasAutomaticChecking = exerciseBlock.HasAutomaticChecking(); var sandbox = (exerciseSlide.Exercise as UniversalExerciseBlock)?.DockerImageName; var submissionId = await CreateInitialSubmission(courseId, exerciseSlide, userCode, language, userId, userName, hasAutomaticChecking, buildResult, serviceScopeFactory); UserExerciseSubmission submissionNoTracking; // Получается позже, чтобы быть максимально обновленным из базы, и чтобы не занимать память надолго и не попасть в 2 поколение var isCourseAdmin = await courseRolesRepo.HasUserAccessToCourse(userId, courseId, CourseRoleType.CourseAdmin); if (buildResult.HasErrors) { submissionNoTracking = await userSolutionsRepo.FindSubmissionByIdNoTracking(submissionId); return(new RunSolutionResponse(SolutionRunStatus.Success) { Submission = SubmissionInfo.Build(submissionNoTracking, null, isCourseAdmin) }); } var executionTimeout = TimeSpan.FromSeconds(exerciseBlock.TimeLimit * 2 + 5); try { if (hasAutomaticChecking) { var priority = exerciseBlock is SingleFileExerciseBlock ? 10 : 0; await userSolutionsRepo.RunAutomaticChecking(submissionId, sandbox, executionTimeout, waitUntilChecked, priority); } } catch (SubmissionCheckingTimeout) { log.Error($"Не смог запустить проверку решения, никто не взял его на проверку за {executionTimeout.TotalSeconds} секунд.\nКурс «{course.Title}», слайд «{exerciseSlide.Title}» ({exerciseSlide.Id})"); await errorsBot.PostToChannelAsync($"Не смог запустить проверку решения, никто не взял его на проверку за {executionTimeout.TotalSeconds} секунд.\nКурс «{course.Title}», слайд «{exerciseSlide.Title}» ({exerciseSlide.Id})\n\nhttps://ulearn.me/Sandbox"); submissionNoTracking = await userSolutionsRepo.FindSubmissionByIdNoTracking(submissionId); var message = submissionNoTracking.AutomaticChecking.Status == AutomaticExerciseCheckingStatus.Running ? "Решение уже проверяется." : "Решение ждет своей очереди на проверку, мы будем пытаться проверить его еще 10 минут."; return(new RunSolutionResponse(SolutionRunStatus.SubmissionCheckingTimeout) { Message = $"К сожалению, мы не смогли оперативно проверить ваше решение. {message}. Просто подождите и обновите страницу.", Submission = SubmissionInfo.Build(submissionNoTracking, null, isCourseAdmin) }); } submissionNoTracking = await userSolutionsRepo.FindSubmissionByIdNoTracking(submissionId); if (!waitUntilChecked) { metricSender.SendCount($"exercise.{exerciseMetricId}.dont_wait_result"); // По вовзращаемому значению нельзя отличить от случая, когда никто не взял на проверку return(new RunSolutionResponse(SolutionRunStatus.Success) { Submission = SubmissionInfo.Build(submissionNoTracking, null, isCourseAdmin) }); } if (!hasAutomaticChecking) { await RunnerSetResultController.SendToReviewAndUpdateScore(submissionNoTracking, courseManager, slideCheckingsRepo, groupsRepo, visitsRepo, metricSender); } // StyleErrors для C# proj и file устанавливаются только здесь, берутся из buildResult. В StyleErrorsResultObserver.ProcessResult попадают только ошибки из docker if (buildResult.HasStyleErrors) { var styleErrors = await ConvertStyleErrors(buildResult); submissionNoTracking.Reviews = await styleErrorsResultObserver.CreateStyleErrorsReviewsForSubmission(submissionId, styleErrors, exerciseMetricId); } var score = await visitsRepo.GetScore(courseId, exerciseSlide.Id, userId); var waitingForManualChecking = submissionNoTracking.ManualCheckings.Any(c => !c.IsChecked) ? true : (bool?)null; var prohibitFurtherManualChecking = submissionNoTracking.ManualCheckings.Any(c => c.ProhibitFurtherManualCheckings); var result = new RunSolutionResponse(SolutionRunStatus.Success) { Score = score, WaitingForManualChecking = waitingForManualChecking, ProhibitFurtherManualChecking = prohibitFurtherManualChecking, Submission = SubmissionInfo.Build(submissionNoTracking, null, isCourseAdmin) }; return(result); }