예제 #1
0
        public async Task SendDeliveriesOfOneType(NotificationTransport transport, NotificationType notificationType, List <NotificationDelivery> deliveries)
        {
            if (deliveries.Count == 0)
            {
                return;
            }

            if (deliveries.Count == 1)
            {
                var delivery = deliveries[0];
                var course   = courseManager.FindCourse(delivery.Notification.CourseId);
                if (course == null)
                {
                    log.Warn($"Can't find course {delivery.Notification.CourseId}");
                    await notificationsRepo.MarkDeliveryAsFailed(delivery);

                    return;
                }

                try
                {
                    await notificationSender.SendAsync(delivery);

                    await notificationsRepo.MarkDeliveryAsSent(delivery.Id);
                }
                catch (Exception e)
                {
                    log.Warn($"Can\'t send notification {delivery.NotificationId} to {delivery.NotificationTransport}: {e}. Will try later");
                    await notificationsRepo.MarkDeliveryAsFailed(delivery);
                }
            }
            else
            {
                try
                {
                    await notificationSender.SendAsync(deliveries);

                    await notificationsRepo.MarkDeliveriesAsSent(deliveries.Select(d => d.Id).ToList());
                }
                catch (Exception e)
                {
                    log.Warn($"Can\'t send multiple notifications [{string.Join(", ", deliveries.Select(d => d.NotificationId))}] to {deliveries[0].NotificationTransport}: {e}. Will try later");
                    await notificationsRepo.MarkDeliveriesAsFailed(deliveries);
                }
            }
        }
예제 #2
0
        public HashSet <string> GetVisibleCourses()
        {
            var appearances = db.UnitAppearances
                              .Where(u => u.PublishTime <= DateTime.Now)
                              .Select(u => new { u.CourseId, u.UnitId })
                              .AsEnumerable()
                              .GroupBy(p => p.CourseId)
                              .Where(g => courseManager.FindCourse(g.Key) != null)
                              .Where(g =>
            {
                var units = courseManager.GetCourse(g.Key).GetUnitsNotSafe().Select(u => u.Id).ToHashSet();
                units.IntersectWith(g.Select(p => p.UnitId));
                return(units.Any());
            })
                              .Select(g => g.Key);

            return(new HashSet <string>(appearances, StringComparer.OrdinalIgnoreCase));
        }
예제 #3
0
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            // Specify a default argument name if none is set by ModelBinderAttribute
            var modelName = bindingContext.BinderModelName;

            if (string.IsNullOrEmpty(modelName))
            {
                modelName = "courseId";
            }

            // Try to fetch the value of the argument by name
            var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

            if (valueProviderResult == ValueProviderResult.None)
            {
                return(Task.CompletedTask);
            }

            bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
            var value = valueProviderResult.FirstValue;

            // Check if the argument value is null or empty
            if (string.IsNullOrEmpty(value))
            {
                return(Task.CompletedTask);
            }

            var model = courseManager.FindCourse(value);

            if (model == null)
            {
                bindingContext.ModelState.TryAddModelError(modelName, $"Course {value} not found");
            }
            bindingContext.Result = model == null?ModelBindingResult.Failed() : ModelBindingResult.Success(model);

            return(Task.CompletedTask);
        }
예제 #4
0
        public async Task <UserExerciseSubmission> AddUserExerciseSubmission(
            string courseId, Guid slideId,
            string code, string compilationError, string output,
            string userId, string executionServiceName, string displayName,
            Language language,
            string sandbox,
            AutomaticExerciseCheckingStatus status = AutomaticExerciseCheckingStatus.Waiting)
        {
            if (string.IsNullOrWhiteSpace(code))
            {
                code = "// no code";
            }
            var hash = (await textsRepo.AddText(code)).Hash;
            var compilationErrorHash = (await textsRepo.AddText(compilationError)).Hash;
            var outputHash           = (await textsRepo.AddText(output)).Hash;
            var exerciseBlock        = (courseManager.FindCourse(courseId)?.FindSlideById(slideId, true) as ExerciseSlide)?.Exercise;

            AutomaticExerciseChecking automaticChecking;

            if (exerciseBlock != null && exerciseBlock.HasAutomaticChecking())
            {
                automaticChecking = new AutomaticExerciseChecking
                {
                    CourseId             = courseId,
                    SlideId              = slideId,
                    UserId               = userId,
                    Timestamp            = DateTime.Now,
                    CompilationErrorHash = compilationErrorHash,
                    IsCompilationError   = !string.IsNullOrWhiteSpace(compilationError),
                    OutputHash           = outputHash,
                    ExecutionServiceName = executionServiceName,
                    DisplayName          = displayName,
                    Status               = status,
                    IsRightAnswer        = false,
                };

                db.AutomaticExerciseCheckings.Add(automaticChecking);
            }
            else
            {
                automaticChecking = null;
            }

            var submission = new UserExerciseSubmission
            {
                CourseId          = courseId,
                SlideId           = slideId,
                UserId            = userId,
                Timestamp         = DateTime.Now,
                SolutionCodeHash  = hash,
                CodeHash          = code.Split('\n').Select(x => x.Trim()).Aggregate("", (x, y) => x + y).GetHashCode(),
                Likes             = new List <Like>(),
                AutomaticChecking = automaticChecking,
                AutomaticCheckingIsRightAnswer = automaticChecking?.IsRightAnswer ?? true,
                Language = language,
                Sandbox  = sandbox
            };

            db.UserExerciseSubmissions.Add(submission);

            await db.SaveChangesAsync();

            return(submission);
        }
예제 #5
0
        private void CreateDeliviesForNotification(Notification notification)
        {
            var notificationType = notification.GetNotificationType();

            log.Info($"Found new notification {notificationType} #{notification.Id}");

            if (!notification.IsActual())
            {
                log.Info($"Notification #{notification.Id}: is not actual more");
                return;
            }

            var blockerNotifications = notification.GetBlockerNotifications(db);

            if (blockerNotifications.Count > 0)
            {
                log.Info(
                    $"There are blocker notifications (this one will not be send if blocker notifications has been already sent to the same transport): " +
                    $"{string.Join(", ", blockerNotifications.Select(n => $"{n.GetNotificationType()} #{n.Id}"))}"
                    );
            }

            Course course = null;

            if (!string.IsNullOrWhiteSpace(notification.CourseId))
            {
                course = courseManager.FindCourse(notification.CourseId);
                if (course == null)
                {
                    return;
                }
            }

            var recipientsIds = notification.GetRecipientsIds(db, course).ToHashSet();

            recipientsIds = FilterUsersWhoNotSeeCourse(notification, recipientsIds);

            log.Info($"Recipients list for notification {notification.Id}: {recipientsIds.Count} user(s)");

            if (recipientsIds.Count == 0)
            {
                return;
            }

            if (recipientsIds.Count > 1000)
            {
                log.Warn($"Recipients list for notification is too big {notification.Id}: {recipientsIds.Count} user(s)");
            }

            var transportsSettings = recipientsIds.Count > 1000 ?
                                     db.NotificationTransportSettings
                                     .Include(s => s.NotificationTransport)
                                     .Where(s => s.CourseId == notification.CourseId &&
                                            s.NotificationType == notificationType &&
                                            !s.NotificationTransport.IsDeleted)
                                     .ToList()
                                     .Where(s => recipientsIds.Contains(s.NotificationTransport.UserId))
                                     .ToList()
                                : db.NotificationTransportSettings
                                     .Include(s => s.NotificationTransport)
                                     .Where(s => s.CourseId == notification.CourseId &&
                                            s.NotificationType == notificationType &&
                                            !s.NotificationTransport.IsDeleted &&
                                            recipientsIds.Contains(s.NotificationTransport.UserId)).ToList();

            var commonTransports = db.NotificationTransports.Where(t => t.UserId == null && t.IsEnabled).ToList();

            var isEnabledByDefault = notificationType.IsEnabledByDefault();

            if (isEnabledByDefault)
            {
                log.Info($"Notification #{notification.Id}. This notification type is enabled by default, so collecting data for it");

                var recipientsTransports = db.NotificationTransports.Where(
                    t => recipientsIds.Contains(t.UserId) &&
                    !t.IsDeleted &&
                    t.IsEnabled
                    ).ToList();
                var notFoundTransports = recipientsTransports.Except(transportsSettings.Select(c => c.NotificationTransport), new NotificationTransportIdComparer());

                foreach (var transport in notFoundTransports)
                {
                    transportsSettings.Add(new NotificationTransportSettings
                    {
                        IsEnabled               = true,
                        NotificationTransport   = transport,
                        NotificationTransportId = transport.Id,
                    });
                }
            }
            else if (notification.IsNotificationForEveryone)
            {
                /* Add notification to all common transports */
                /* It's used i.e. for new-comment notification which should be sent to everyone in the course */
                log.Info($"Notification #{notification.Id}. This notification type is sent to everyone, so add it to all common transports ({commonTransports.Count} transport(s)):");
                foreach (var commonTransport in commonTransports)
                {
                    log.Info($"Common transport {commonTransport}");
                    transportsSettings.Add(new NotificationTransportSettings
                    {
                        IsEnabled               = true,
                        NotificationTransport   = commonTransport,
                        NotificationTransportId = commonTransport.Id,
                    });
                }
            }

            var blockerNotificationsSentToTransports = new HashSet <int>(
                GetNotificationsDeliveries(blockerNotifications.Select(n => n.Id), transportsSettings.Select(s => s.NotificationTransportId))
                .Select(d => d.NotificationTransportId)
                );

            if (blockerNotificationsSentToTransports.Count > 0)
            {
                log.Info(
                    "Blocked notifications have been already sent to follow transports:" +
                    $"{string.Join(", ", blockerNotificationsSentToTransports)}"
                    );
            }

            var now = DateTime.Now;

            foreach (var transportSettings in transportsSettings)
            {
                log.Info($"Notification #{notification.Id}: add delivery to {transportSettings.NotificationTransport}, isEnabled: {transportSettings.IsEnabled}");
                if (!transportSettings.IsEnabled)
                {
                    continue;
                }

                if (!transportSettings.NotificationTransport.IsEnabled)
                {
                    log.Info($"Transport {transportSettings.NotificationTransport} is fully disabled, ignore it");
                    continue;
                }

                /* Always ignore to send notification to user initiated this notification */
                if (transportSettings.NotificationTransport.UserId == notification.InitiatedById)
                {
                    log.Info($"Don't sent notification to the transport {transportSettings.NotificationTransport} because it has been initiated by this user");
                    continue;
                }

                if (blockerNotificationsSentToTransports.Contains(transportSettings.NotificationTransportId))
                {
                    log.Info($"Some blocker notification already has been sent to transport {transportSettings.NotificationTransport}, ignore it");
                    continue;
                }

                log.Info($"Notification #{notification.Id}: add delivery to {transportSettings.NotificationTransport}, sending at {now}");
                db.NotificationDeliveries.Add(new NotificationDelivery
                {
                    Notification            = notification,
                    NotificationTransportId = transportSettings.NotificationTransportId,
                    CreateTime  = now,
                    NextTryTime = now,
                    FailsCount  = 0,
                    Status      = NotificationDeliveryStatus.NotSent,
                });
            }
        }
예제 #6
0
        public async Task <ActionResult> SlideById(string courseId, string slideId = "", int?checkQueueItemId = null, int?version = null, int autoplay = 0)
        {
            if (slideId.Contains("_"))
            {
                slideId = slideId.Substring(slideId.LastIndexOf('_') + 1);
            }

            // По крайней мере одно из мест использования groupsIds: переход на следующее ревью после выполнения предыдущего.
            var groupsIds = Request.GetMultipleValuesFromQueryString("group");

            if (!Guid.TryParse(slideId, out var slideGuid))
            {
                return(HttpNotFound());
            }

            if (string.IsNullOrWhiteSpace(courseId))
            {
                return(RedirectToAction("Index", "Home"));
            }

            var course = courseManager.FindCourse(courseId);

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

            var visibleUnitIds = unitsRepo.GetVisibleUnitIds(course, User);
            var visibleUnits   = course.GetUnits(visibleUnitIds);
            var isGuest        = !User.Identity.IsAuthenticated;
            var isInstructor   = !isGuest && User.HasAccessFor(course.Id, CourseRole.Instructor);

            var slide = slideGuid == Guid.Empty
                                ? GetInitialSlideForStartup(courseId, visibleUnits, isInstructor)
                                : course.FindSlideById(slideGuid, isInstructor);

            if (slide == null)
            {
                var instructorNote = course.FindInstructorNoteById(slideGuid);
                if (instructorNote != null && isInstructor)
                {
                    slide = instructorNote.Slide;
                }
            }

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

            AbstractManualSlideChecking queueItem = null;
            var isManualCheckingReadonly          = false;

            if (User.HasAccessFor(courseId, CourseRole.Instructor) && checkQueueItemId != null)
            {
                if (slide is QuizSlide)
                {
                    queueItem = slideCheckingsRepo.FindManualCheckingById <ManualQuizChecking>(checkQueueItemId.Value);
                }
                if (slide is ExerciseSlide)
                {
                    queueItem = slideCheckingsRepo.FindManualCheckingById <ManualExerciseChecking>(checkQueueItemId.Value);
                }

                if (queueItem == null)
                {
                    /* It's possible when checking has not been fully checked, lock has been released, but after it user re-send his solution and we removed old waiting checking */
                    var fakeQueueItem = slide is QuizSlide ? (AbstractManualSlideChecking) new ManualQuizChecking() : new ManualExerciseChecking();
                    return(RedirectToAction("CheckingQueue", "Admin", new
                    {
                        courseId = courseId,
                        message = "checking_removed"
                    }));
                }
            }

            var model = isGuest ?
                        CreateGuestCoursePageModel(course, slide, autoplay > 0) :
                        await CreateCoursePageModel(course, slide, queueItem, version, groupsIds, autoplay > 0, isManualCheckingReadonly);

            if (!string.IsNullOrEmpty(Request.QueryString["error"]))
            {
                model.Error = Request.QueryString["error"];
            }

            if (!visibleUnits.Contains(model.Slide.Info.Unit))
            {
                return(HttpNotFound("Slide is hidden " + slideGuid));
            }
            return(View("Slide", model));
        }