コード例 #1
0
        public async Task SendAsync(NotificationDelivery notificationDelivery)
        {
            var transport = notificationDelivery.NotificationTransport;

            metricSender.SendCount($"send_notification.{notificationDelivery.Notification.GetNotificationType()}");

            if (transport is MailNotificationTransport || transport is TelegramNotificationTransport)
            {
                await SendAsync((dynamic)transport, notificationDelivery);
            }
        }
コード例 #2
0
        public RunningResults RunCsc(string submissionCompilationDirectory)
        {
            log.Info($"Запускаю проверку C#-решения {submission.Id}, компилирую с помощью Roslyn");

            var res = AssemblyCreator.CreateAssemblyWithRoslyn((FileRunnerSubmission)submission, submissionCompilationDirectory, settings.CompilationTimeLimit);

            try
            {
                metricSender.SendCount("exercise.compilation.csc.elapsed", (int)res.Elapsed.TotalMilliseconds);
                var diagnostics       = res.EmitResult.Diagnostics;
                var compilationOutput = diagnostics.DumpCompilationOutput();

                if (diagnostics.HasErrors())
                {
                    log.Error($"Ошибка компиляции:\n{compilationOutput}");
                    return(new RunningResults(Verdict.CompilationError, compilationOutput: compilationOutput));
                }

                if (!submission.NeedRun)
                {
                    return(new RunningResults(Verdict.Ok));
                }

                return(RunSandbox($"\"{Path.GetFullPath(res.PathToAssembly)}\" {submission.Id}"));
            }
            finally
            {
                SafeRemoveFile(res.PathToAssembly);
            }
        }
コード例 #3
0
        protected async Task <bool> SendConfirmationEmail(ApplicationUser user)
        {
            metricSender.SendCount("email_confirmation.send_confirmation_email.try");
            var confirmationUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, email = user.Email, signature = GetEmailConfirmationSignature(user.Email) }, "https");
            var subject         = "Подтверждение адреса";

            var messageInfo = new MessageSentInfo
            {
                RecipientAddress = user.Email,
                RecipientName    = user.VisibleName,
                Subject          = subject,
                TemplateId       = spamTemplateId,
                Variables        = new Dictionary <string, object>
                {
                    { "title", subject },
                    { "content", $"<h2>Привет, {user.VisibleName.EscapeHtml()}!</h2><p>Подтверди адрес электронной почты, нажав на кнопку:</p>" },
                    { "text_content", $"Привет, {user.VisibleName}!\nПодтверди адрес электронной почты, нажав на кнопку:" },
                    { "button", true },
                    { "button_link", confirmationUrl },
                    { "button_text", "Подтвердить адрес" },
                    {
                        "content_after_button",
                        "<p>Подтвердив адрес, ты сможешь восстановить доступ к своему аккаунту " +
                        "в любой момент, а также получать уведомления об ответах на свои комментарии и других важных событиях</p>" +
                        "<p>Мы не подпиcываем ни на какую периодическую рассылку, " +
                        "а все уведомления можно выключить в профиле.</p><p>" +
                        "Если ссылка для подтверждения почты не работает, просто скопируй адрес " +
                        $"и вставь его в адресную строку браузера: <a href=\"{confirmationUrl}\">{confirmationUrl}</a></p>"
                    }
                }
            };

            try
            {
                await spamClient.SentMessageAsync(spamChannelId, messageInfo);
            }
            catch (Exception e)
            {
                log.Error($"Не могу отправить письмо для подтверждения адреса на {user.Email}", e);
                return(false);
            }
            metricSender.SendCount("email_confirmation.send_confirmation_email.success");

            await usersRepo.UpdateLastConfirmationEmailTime(user);

            return(true);
        }
コード例 #4
0
        public async Task <ActionResult> Index(string username)
        {
            metricSender.SendCount("restore_password.try");
            var users = await FindUsers(username);

            var answer = new RestorePasswordModel
            {
                UserName = username
            };

            if (!users.Any())
            {
                answer.Messages.Add(new Message($"Пользователь {username} не найден"));
                return(View(answer));
            }

            metricSender.SendCount("restore_password.found_users");

            foreach (var user in users)
            {
                if (string.IsNullOrWhiteSpace(user.Email))
                {
                    answer.Messages.Add(new Message($"У пользователя {user.UserName} не указана электронная почта"));
                    continue;
                }

                if (!user.EmailConfirmed)
                {
                    answer.Messages.Add(new Message($"У пользователя {user.UserName} не подтверждена электронная почта"));
                    continue;
                }

                var requestId = await requestRepo.CreateRequest(user.Id);

                if (requestId == null)
                {
                    answer.Messages.Add(new Message($"Слишком частые запросы для пользователя {user.UserName}. Попробуйте ещё раз через несколько минут"));
                    continue;
                }

                await SendRestorePasswordEmail(requestId, user);

                answer.Messages.Add(new Message($"Письмо с инструкцией по восстановлению пароля для пользователя {user.UserName} отправлено вам на почту", false));
            }

            return(View(answer));
        }
コード例 #5
0
ファイル: TelegramSender.cs プロジェクト: Griboedoff/Ulearn
        public async Task SendMessageAsync(long chatId, string html, TelegramButton button = null)
        {
            metricSender.SendCount("send_to_telegram.try");
            metricSender.SendCount($"send_to_telegram.try.to.{chatId}");
            html = PrepareHtmlForTelegram(html);
            log.Info($"Try to send message to telegram chat {chatId}, html: {html.Replace("\n", @" \\ ")}" + (button != null ? $", button: {button}" : ""));

            InlineKeyboardMarkup replyMarkup = null;

            if (button != null)
            {
                replyMarkup = new InlineKeyboardMarkup(new[] { InlineKeyboardButton.WithUrl(button.Text, button.Link) });
            }

            try
            {
                await bot.SendTextMessageAsync(chatId, html, parseMode : ParseMode.Html, replyMarkup : replyMarkup);
            }
            catch (Exception e)
            {
                log.Error($"Can\'t send message to telegram chat {chatId}", e);

                metricSender.SendCount("send_to_telegram.fail");
                metricSender.SendCount($"send_to_telegram.fail.to.{chatId}");

                if (e is ApiRequestException)
                {
                    var apiRequestException = (ApiRequestException)e;
                    var isBotBlockedByUser  = apiRequestException.Message.Contains("bot was blocked by the user");
                    if (isBotBlockedByUser)
                    {
                        metricSender.SendCount("send_to_telegram.fail.blocked_by_user");
                        metricSender.SendCount($"send_to_telegram.fail.blocked_by_user.to.{chatId}");
                    }
                }
                throw;
            }
            metricSender.SendCount("send_to_telegram.success");
            metricSender.SendCount($"send_to_telegram.success.to.{chatId}");
        }
コード例 #6
0
        public async Task SendEmailAsync(string to, string subject, string textContent = null, string htmlContent = null, EmailButton button = null, string textContentAfterButton = null, string htmlContentAfterButton = null)
        {
            var recipientEmailMetricName = to.ToLower().Replace(".", "_");

            metricSender.SendCount("send_email.try");
            metricSender.SendCount($"send_email.try.to.{recipientEmailMetricName}");

            var messageInfo = new MessageSentInfo
            {
                RecipientAddress = to,
                Subject          = subject,
            };

            if (!string.IsNullOrEmpty(templateId))
            {
                messageInfo.TemplateId = templateId;
                messageInfo.Variables  = new Dictionary <string, object>
                {
                    { "title", subject },
                    { "content", htmlContent },
                    { "text_content", textContent },
                    { "button", button != null },
                    { "button_link", button?.Link },
                    { "button_text", button?.Text },
                    { "content_after_button", htmlContentAfterButton },
                    { "text_content_after_button", textContentAfterButton },
                };
            }
            else
            {
                messageInfo.Html = htmlContent;
                messageInfo.Text = textContent;
            }

            log.Info($"Try to send message to {to} with subject {subject}, text: {textContent?.Replace("\n", @" \\ ")}");
            try
            {
                await client.SentMessageAsync(channelId, messageInfo);
            }
            catch (Exception e)
            {
                log.Error($"Can\'t send message via Spam.API to {to} with subject {subject}", e);
                metricSender.SendCount("send_email.fail");
                metricSender.SendCount($"send_email.fail.to.{recipientEmailMetricName}");
                throw;
            }

            metricSender.SendCount("send_email.success");
            metricSender.SendCount($"send_email.success.to.{recipientEmailMetricName}");
        }
コード例 #7
0
ファイル: QuizController.cs プロジェクト: FuryThrue/Ulearn
        public ActionResult Quiz(QuizSlide slide, string courseId, string userId, bool isGuest, bool isLti = false, ManualQuizChecking manualQuizCheckQueueItem = null, int?send = null)
        {
            metricSender.SendCount("quiz.show");
            if (isLti)
            {
                metricSender.SendCount("quiz.show.lti");
            }
            metricSender.SendCount($"quiz.show.{courseId}");
            metricSender.SendCount($"quiz.show.{courseId}.{slide.Id}");

            var course = courseManager.FindCourse(courseId);

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

            if (isGuest)
            {
                metricSender.SendCount("quiz.show.to.guest");
                return(PartialView(GuestQuiz(course, slide)));
            }
            var slideId          = slide.Id;
            var maxTriesCount    = GetMaxTriesCount(courseId, slide);
            var state            = GetQuizState(courseId, userId, slideId);
            var quizState        = state.Item1;
            var tryNumber        = state.Item2;
            var resultsForQuizes = GetResultForQuizes(courseId, userId, slideId, state.Item1);

            log.Info($"Создаю тест для пользователя {userId} в слайде {courseId}:{slide.Id}, isLti = {isLti}");

            var quizVersion = quizzesRepo.GetLastQuizVersion(courseId, slideId);

            if (quizState != QuizState.NotPassed)
            {
                quizVersion = userQuizzesRepo.FindQuizVersionFromUsersAnswer(courseId, slideId, userId);
            }

            /* If we haven't quiz version in database, create it */
            if (quizVersion == null)
            {
                quizVersion = quizzesRepo.AddQuizVersionIfNeeded(courseId, slide);
            }

            /* Restore quiz slide from version stored in the database */
            var quiz = quizVersion.GetRestoredQuiz(course, course.FindUnitBySlideId(slide.Id));

            slide = new QuizSlide(slide.Info, quiz);

            if (quizState == QuizState.Subtotal)
            {
                var score = resultsForQuizes?.AsEnumerable().Sum(res => res.Value) ?? 0;
                /* QuizState.Subtotal is partially obsolete. If user fully solved quiz, then show answers. Else show empty quiz for the new try... */
                if (score == quiz.MaxScore)
                {
                    quizState = QuizState.Total;
                }
                /* ... and show last try's answers only if argument `send` has been passed in query */
                else if (!send.HasValue)
                {
                    quizState = QuizState.NotPassed;
                    /* ... if we will show answers from last try then drop quiz */
                    userQuizzesRepo.DropQuiz(courseId, userId, slideId);
                }
            }

            var userAnswers     = userQuizzesRepo.GetAnswersForShowOnSlide(courseId, slide, userId);
            var canUserFillQuiz = CanUserFillQuiz(quizState);

            var questionAnswersFrequency = new DefaultDictionary <string, DefaultDictionary <string, int> >();

            if (User.HasAccessFor(courseId, CourseRole.CourseAdmin))
            {
                questionAnswersFrequency = quiz.Blocks.OfType <ChoiceBlock>().ToDictionary(
                    block => block.Id,
                    block => userQuizzesRepo.GetAnswersFrequencyForChoiceBlock(courseId, slide.Id, block.Id).ToDefaultDictionary()
                    ).ToDefaultDictionary();
            }

            var model = new QuizModel
            {
                Course                   = course,
                Slide                    = slide,
                QuizState                = quizState,
                TryNumber                = tryNumber,
                MaxTriesCount            = maxTriesCount,
                ResultsForQuizes         = resultsForQuizes,
                AnswersToQuizes          = userAnswers,
                IsLti                    = isLti,
                ManualQuizCheckQueueItem = manualQuizCheckQueueItem,
                CanUserFillQuiz          = canUserFillQuiz,
                GroupsIds                = Request.GetMultipleValuesFromQueryString("group"),
                QuestionAnswersFrequency = questionAnswersFrequency,
            };

            return(PartialView(model));
        }
コード例 #8
0
        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);
        }
コード例 #9
0
ファイル: QuizController.cs プロジェクト: PhonkX/uLearn
        public ActionResult Quiz(QuizSlide slide, string courseId, string userId, bool isGuest, bool isLti = false, ManualQuizChecking manualQuizCheckQueueItem = null, int?send = null, bool attempt = false)
        {
            metricSender.SendCount("quiz.show");
            if (isLti)
            {
                metricSender.SendCount("quiz.show.lti");
            }
            metricSender.SendCount($"quiz.show.{courseId}");
            metricSender.SendCount($"quiz.show.{courseId}.{slide.Id}");

            var course = courseManager.FindCourse(courseId);

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

            if (isGuest)
            {
                metricSender.SendCount("quiz.show.to.guest");
                return(PartialView(GuestQuiz(course, slide)));
            }
            var slideId          = slide.Id;
            var maxAttemptsCount = GetMaxAttemptsCount(courseId, slide);
            var userScores       = GetUserScoresForBlocks(courseId, userId, slideId, manualQuizCheckQueueItem?.Submission);

            var score = userScores?.AsEnumerable().Sum(res => res.Value) ?? 0;

            var state = GetQuizState(courseId, userId, slideId, score, slide.MaxScore, manualQuizCheckQueueItem?.Submission);

            log.Info($"Показываю тест для пользователя {userId} в слайде {courseId}:{slide.Id}, isLti = {isLti}");

            /* If it's manual checking, change quiz state to IsChecking for correct rendering */
            if (manualQuizCheckQueueItem != null)
            {
                state.Status = QuizStatus.IsCheckingByInstructor;
            }

            /* For manually checked quizzes show last attempt's answers until ?attempt=true is defined in query string */
            if (slide.ManualChecking && manualQuizCheckQueueItem == null && state.Status == QuizStatus.ReadyToSend && state.UsedAttemptsCount > 0 && !attempt)
            {
                state.Status = QuizStatus.Sent;
            }

            /* We also want to show user's answer if user sent answers just now */
            if (state.Status == QuizStatus.ReadyToSend && send.HasValue)
            {
                state.Status = QuizStatus.Sent;
            }

            var userAnswers     = userQuizzesRepo.GetAnswersForShowingOnSlide(courseId, slide, userId, manualQuizCheckQueueItem?.Submission);
            var canUserFillQuiz = CanUserFillQuiz(state.Status);

            var questionAnswersFrequency = new DefaultDictionary <string, DefaultDictionary <string, int> >();

            if (User.HasAccessFor(courseId, CourseRole.CourseAdmin))
            {
                questionAnswersFrequency = slide.Blocks.OfType <ChoiceBlock>().ToDictionary(
                    block => block.Id,
                    block => userQuizzesRepo.GetAnswersFrequencyForChoiceBlock(courseId, slide.Id, block.Id).ToDefaultDictionary()
                    ).ToDefaultDictionary();
            }

            var model = new QuizModel
            {
                Course              = course,
                Slide               = slide,
                QuizState           = state,
                MaxAttemptsCount    = maxAttemptsCount,
                UserScores          = userScores,
                AnswersToQuizzes    = userAnswers,
                IsLti               = isLti,
                Checking            = manualQuizCheckQueueItem,
                ManualCheckingsLeft = manualQuizCheckQueueItem != null?GetManualCheckingsCountInQueue(course, slide) : 0,
                                          CanUserFillQuiz          = canUserFillQuiz,
                                          GroupsIds                = Request.GetMultipleValuesFromQueryString("group"),
                                          QuestionAnswersFrequency = questionAnswersFrequency,
            };

            return(PartialView(model));
        }