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); } }
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); } }
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); }
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)); }
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}"); }
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}"); }
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)); }
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 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)); }