Ejemplo n.º 1
0
        public ExerciseBlockResponse(AbstractExerciseBlock exerciseBlock,
                                     ExerciseSlideRendererContext context)
        {
            var reviewId2Comments = context.CodeReviewComments
                                    ?.GroupBy(c => c.ReviewId)
                                    .ToDictionary(g => g.Key, g => g.AsEnumerable());

            if (exerciseBlock is PolygonExerciseBlock polygonExerciseBlock)
            {
                Languages       = PolygonExerciseBlock.LanguagesInfo.Keys.ToArray();
                LanguageInfo    = PolygonExerciseBlock.LanguagesInfo;
                DefaultLanguage = polygonExerciseBlock.DefaultLanguage;
            }
            else
            {
                Languages       = exerciseBlock.Language != null ? new[] { exerciseBlock.Language.Value } : new Language[0];
                LanguageInfo    = null;
                DefaultLanguage = null;
            }

            RenderedHints       = exerciseBlock.Hints.Select(h => RenderHtmlWithHint(h, context.SlideFile)).ToArray();
            ExerciseInitialCode = exerciseBlock.ExerciseInitialCode.RemoveEmptyLinesFromStart().TrimEnd().EnsureEnoughLines(4);
            HideSolutions       = exerciseBlock.HideShowSolutionsButton;
            ExpectedOutput      = exerciseBlock.HideExpectedOutputOnError ? null : exerciseBlock.ExpectedOutput?.NormalizeEoln();
            AttemptsStatistics  = context.AttemptsStatistics;
            Submissions         = context.Submissions
                                  .EmptyIfNull()
                                  .Select(s => SubmissionInfo.Build(s, reviewId2Comments, context.CanSeeCheckerLogs))
                                  .ToList();
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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);
        }