module Jobs.Handler.QueueNotification ( dispatchJobQueueNotification ) where import Import import Data.List (nub) import Jobs.Types import qualified Database.Esqueleto as E import qualified Database.Esqueleto.Utils as E import Utils.Sql import Jobs.Queue import qualified Data.Set as Set dispatchJobQueueNotification :: Notification -> Handler () dispatchJobQueueNotification jNotification = runDBJobs . setSerializable $ do candidates <- hoist lift $ determineNotificationCandidates jNotification nClass <- hoist lift $ classifyNotification jNotification mapM_ (queueDBJob . flip JobSendNotification jNotification) $ do Entity uid User{userNotificationSettings} <- candidates guard $ notificationAllowed userNotificationSettings nClass return uid determineNotificationCandidates :: Notification -> DB [Entity User] determineNotificationCandidates NotificationSubmissionRated{..} = E.select . E.from $ \(user `E.InnerJoin` submissionUser) -> do E.on $ user E.^. UserId E.==. submissionUser E.^. SubmissionUserUser E.where_ $ submissionUser E.^. SubmissionUserSubmission E.==. E.val nSubmission return user determineNotificationCandidates NotificationSheetActive{..} = E.select . E.from $ \(user `E.InnerJoin` courseParticipant `E.InnerJoin` sheet) -> do E.on $ sheet E.^. SheetCourse E.==. courseParticipant E.^. CourseParticipantCourse E.on $ user E.^. UserId E.==. courseParticipant E.^. CourseParticipantUser E.where_ $ sheet E.^. SheetId E.==. E.val nSheet return user determineNotificationCandidates NotificationSheetSoonInactive{..} = E.select . E.from $ \(user `E.InnerJoin` courseParticipant `E.InnerJoin` sheet) -> do E.on $ sheet E.^. SheetCourse E.==. courseParticipant E.^. CourseParticipantCourse E.on $ user E.^. UserId E.==. courseParticipant E.^. CourseParticipantUser E.where_ $ sheet E.^. SheetId E.==. E.val nSheet return user determineNotificationCandidates NotificationSheetInactive{..} = E.select . E.from $ \(user `E.InnerJoin` lecturer `E.InnerJoin` sheet) -> do E.on $ lecturer E.^. LecturerCourse E.==. sheet E.^. SheetCourse E.on $ lecturer E.^. LecturerUser E.==. user E.^. UserId E.where_ $ sheet E.^. SheetId E.==. E.val nSheet return user determineNotificationCandidates NotificationCorrectionsAssigned{..} = selectList [UserId ==. nUser] [] determineNotificationCandidates NotificationCorrectionsNotDistributed{nSheet} = E.select . E.from $ \(user `E.InnerJoin` lecturer `E.InnerJoin` sheet) -> do E.on $ lecturer E.^. LecturerCourse E.==. sheet E.^. SheetCourse E.on $ lecturer E.^. LecturerUser E.==. user E.^. UserId E.where_ $ sheet E.^. SheetId E.==. E.val nSheet return user determineNotificationCandidates NotificationUserRightsUpdate{..} = do -- always send to affected user affectedUser <- selectList [UserId ==. nUser] [] -- send to same-school admins only if there was an update currentAdminSchools <- setOf (folded . _entityVal . _userFunctionSchool) <$> selectList [UserFunctionUser ==. nUser, UserFunctionFunction ==. SchoolAdmin] [] let oldAdminSchools = setOf (folded . filtered ((== SchoolAdmin) . view _1) . _2 . from _SchoolId) nOriginalRights newAdminSchools = currentAdminSchools `Set.difference` oldAdminSchools affectedAdmins <- E.select . E.from $ \(user `E.InnerJoin` admin) -> do E.on $ admin E.^. UserFunctionUser E.==. user E.^. UserId E.where_ $ admin E.^. UserFunctionSchool `E.in_` E.valList (Set.toList newAdminSchools) E.&&. admin E.^. UserFunctionFunction E.==. E.val SchoolAdmin return user return . nub $ affectedUser <> affectedAdmins determineNotificationCandidates NotificationUserAuthModeUpdate{..} = selectList [UserId ==. nUser] [] determineNotificationCandidates notif@NotificationExamResult{..} = do lastExec <- fmap (fmap $ cronLastExecTime . entityVal) . getBy . UniqueCronLastExec . toJSON $ JobQueueNotification notif E.select . E.from $ \(examResult `E.InnerJoin` user) -> do E.on $ examResult E.^. ExamResultUser E.==. user E.^. UserId E.where_ $ examResult E.^. ExamResultExam E.==. E.val nExam whenIsJust lastExec $ \lastExec' -> E.where_ $ examResult E.^. ExamResultLastChanged E.>. E.val lastExec' return user determineNotificationCandidates NotificationAllocationStaffRegister{..} = do Allocation{..} <- getJust nAllocation E.select . E.from $ \(user `E.InnerJoin` userFunction) -> do E.on $ user E.^. UserId E.==. userFunction E.^. UserFunctionUser E.&&. userFunction E.^. UserFunctionSchool E.==. E.val allocationSchool E.&&. userFunction E.^. UserFunctionFunction E.==. E.val SchoolLecturer E.where_ . E.exists . E.from $ \userSchool -> E.where_ $ userSchool E.^. UserSchoolUser E.==. user E.^. UserId E.&&. userSchool E.^. UserSchoolSchool E.==. E.val allocationSchool E.&&. E.not_ (userSchool E.^. UserSchoolIsOptOut) E.where_ . E.not_ . E.exists . E.from $ \(course `E.InnerJoin` lecturer) -> do E.on $ lecturer E.^. LecturerCourse E.==. course E.^. CourseId E.&&. lecturer E.^. LecturerUser E.==. user E.^. UserId E.where_ . E.exists . E.from $ \allocationCourse -> E.where_ $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val nAllocation return user determineNotificationCandidates NotificationAllocationAllocation{..} = E.select . E.from $ \(user `E.InnerJoin` lecturer `E.InnerJoin` course `E.InnerJoin` allocationCourse) -> do E.on $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val nAllocation E.on $ course E.^. CourseId E.==. lecturer E.^. LecturerCourse E.on $ lecturer E.^. LecturerUser E.==. user E.^. UserId E.where_ . E.not_ . E.exists . E.from $ \application -> E.where_ $ application E.^. CourseApplicationAllocation E.==. E.val (Just nAllocation) E.&&. application E.^. CourseApplicationCourse E.==. course E.^. CourseId E.&&. application E.^. CourseApplicationUser E.!=. user E.^. UserId E.&&. E.not_ (E.isNothing $ application E.^. CourseApplicationRatingTime) E.where_ . E.exists . E.from $ \application -> E.where_ $ application E.^. CourseApplicationAllocation E.==. E.val (Just nAllocation) E.&&. application E.^. CourseApplicationCourse E.==. course E.^. CourseId E.&&. application E.^. CourseApplicationUser E.!=. user E.^. UserId return user determineNotificationCandidates NotificationAllocationUnratedApplications{..} = E.select . E.from $ \(user `E.InnerJoin` lecturer `E.InnerJoin` course `E.InnerJoin` allocationCourse) -> do E.on $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val nAllocation E.on $ course E.^. CourseId E.==. lecturer E.^. LecturerCourse E.on $ lecturer E.^. LecturerUser E.==. user E.^. UserId E.where_ . E.exists . E.from $ \application -> E.where_ $ application E.^. CourseApplicationAllocation E.==. E.val (Just nAllocation) E.&&. application E.^. CourseApplicationCourse E.==. course E.^. CourseId E.&&. application E.^. CourseApplicationUser E.!=. user E.^. UserId E.&&. E.isNothing (application E.^. CourseApplicationRatingTime) return user determineNotificationCandidates NotificationAllocationRegister{..} = do Allocation{..} <- getJust nAllocation E.select . E.from $ \user -> do E.where_ . E.exists . E.from $ \userSchool -> E.where_ $ userSchool E.^. UserSchoolUser E.==. user E.^. UserId E.&&. userSchool E.^. UserSchoolSchool E.==. E.val allocationSchool E.&&. E.not_ (userSchool E.^. UserSchoolIsOptOut) E.where_ . E.not_ . E.exists . E.from $ \application -> E.where_ $ application E.^. CourseApplicationAllocation E.==. E.val (Just nAllocation) E.&&. application E.^. CourseApplicationUser E.==. user E.^. UserId return user determineNotificationCandidates NotificationAllocationOutdatedRatings{..} = E.select . E.from $ \(user `E.InnerJoin` lecturer `E.InnerJoin` course `E.InnerJoin` allocationCourse) -> do E.on $ allocationCourse E.^. AllocationCourseCourse E.==. course E.^. CourseId E.&&. allocationCourse E.^. AllocationCourseAllocation E.==. E.val nAllocation E.on $ course E.^. CourseId E.==. lecturer E.^. LecturerCourse E.on $ lecturer E.^. LecturerUser E.==. user E.^. UserId E.where_ . E.exists . E.from $ \application -> E.where_ $ application E.^. CourseApplicationAllocation E.==. E.val (Just nAllocation) E.&&. application E.^. CourseApplicationCourse E.==. course E.^. CourseId E.&&. application E.^. CourseApplicationUser E.!=. user E.^. UserId E.&&. E.maybe E.false (E.<. application E.^. CourseApplicationTime) (application E.^. CourseApplicationRatingTime) return user classifyNotification :: Notification -> DB NotificationTrigger classifyNotification NotificationSubmissionRated{..} = do Sheet{sheetType} <- belongsToJust submissionSheet =<< getJust nSubmission return $ case sheetType of NotGraded -> NTSubmissionRated _other -> NTSubmissionRatedGraded classifyNotification NotificationSheetActive{} = return NTSheetActive classifyNotification NotificationSheetSoonInactive{} = return NTSheetSoonInactive classifyNotification NotificationSheetInactive{} = return NTSheetInactive classifyNotification NotificationCorrectionsAssigned{} = return NTCorrectionsAssigned classifyNotification NotificationCorrectionsNotDistributed{} = return NTCorrectionsNotDistributed classifyNotification NotificationUserRightsUpdate{} = return NTUserRightsUpdate classifyNotification NotificationUserAuthModeUpdate{} = return NTUserAuthModeUpdate classifyNotification NotificationExamResult{} = return NTExamResult classifyNotification NotificationAllocationStaffRegister{} = return NTAllocationStaffRegister classifyNotification NotificationAllocationAllocation{} = return NTAllocationAllocation classifyNotification NotificationAllocationRegister{} = return NTAllocationRegister classifyNotification NotificationAllocationOutdatedRatings{} = return NTAllocationOutdatedRatings classifyNotification NotificationAllocationUnratedApplications{} = return NTAllocationUnratedApplications