155 lines
6.6 KiB
Haskell
155 lines
6.6 KiB
Haskell
module Handler.Exam.AddUser
|
|
( getEAddUserR, postEAddUserR
|
|
) where
|
|
|
|
import Import hiding (Option(..))
|
|
import Handler.Exam.RegistrationInvite
|
|
|
|
import Handler.Utils
|
|
import Handler.Utils.Exam
|
|
import Handler.Utils.Invitations
|
|
|
|
import qualified Data.Set as Set
|
|
|
|
import Data.Semigroup (Option(..))
|
|
|
|
import Control.Monad.Error.Class (MonadError(..))
|
|
|
|
import Jobs.Queue
|
|
|
|
import Generics.Deriving.Monoid
|
|
|
|
|
|
data AddRecipientsResult = AddRecipientsResult
|
|
{ aurAlreadyRegistered
|
|
, aurNoCourseRegistration
|
|
, aurSuccess
|
|
, aurSuccessCourse :: [UserEmail]
|
|
} deriving (Read, Show, Generic, Typeable)
|
|
|
|
instance Semigroup AddRecipientsResult where
|
|
(<>) = mappenddefault
|
|
|
|
instance Monoid AddRecipientsResult where
|
|
mempty = memptydefault
|
|
mappend = (<>)
|
|
|
|
|
|
getEAddUserR, postEAddUserR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> Handler Html
|
|
getEAddUserR = postEAddUserR
|
|
postEAddUserR tid ssh csh examn = do
|
|
eEnt@(Entity eid Exam{..}) <- runDB $ fetchExam tid ssh csh examn
|
|
((usersToEnlist,formWgt),formEncoding) <- runFormPost . renderWForm FormStandard $ do
|
|
now <- liftIO getCurrentTime
|
|
occurrences <- liftHandler . runDB $ selectList [ExamOccurrenceExam ==. eid] []
|
|
|
|
let
|
|
localNow = utcToLocalTime now
|
|
tomorrowEndOfDay = case localTimeToUTC (LocalTime (addDays 2 $ localDay localNow) midnight) of
|
|
LTUUnique utc' _ -> utc'
|
|
_other -> UTCTime (addDays 2 $ utctDay now) 0
|
|
earliestDate = getOption . fmap getMin $ mconcat
|
|
[ Option $ Min <$> examStart
|
|
, foldMap (Option . Just . Min . examOccurrenceStart . entityVal) occurrences
|
|
]
|
|
modifiedEarliestDate = earliestDate <&> \earliestDate'@(utcToLocalTime -> localEarliestDate')
|
|
-> case localTimeToUTC (LocalTime (addDays (-1) $ localDay localEarliestDate') midnight) of
|
|
LTUUnique utc' _ -> utc'
|
|
_other -> UTCTime (addDays (-1) $ utctDay earliestDate') 0
|
|
defDeadline
|
|
| Just registerTo <- examRegisterTo
|
|
, registerTo > now
|
|
= registerTo
|
|
| Just earliestDate' <- modifiedEarliestDate
|
|
= max tomorrowEndOfDay earliestDate'
|
|
| otherwise
|
|
= tomorrowEndOfDay
|
|
|
|
deadline <- wreq utcTimeField (fslI MsgExamRegistrationInviteDeadline) (Just defDeadline)
|
|
enlist <- wpopt checkBoxField (fslI MsgExamRegistrationEnlistDirectly & setTooltip MsgExamRegistrationEnlistDirectlyTip) (Just False)
|
|
registerCourse <- wpopt checkBoxField (fslI MsgExamRegistrationRegisterCourse & setTooltip MsgExamRegistrationRegisterCourseTip) (Just False)
|
|
occurrence <- wopt (examOccurrenceField eid) (fslI MsgTableExamOccurrence) Nothing
|
|
mr <- getMessageRender
|
|
users <- wreq (multiUserInvitationField . maybe MUIAlwaysInvite (const $ MUILookupAnyUser Nothing) $ formResultToMaybe enlist)
|
|
(fslpI MsgExamRegistrationInviteField (mr MsgLdapIdentificationOrEmail) & setTooltip MsgMultiEmailFieldTip) Nothing
|
|
return $ (,,,) <$> deadline <*> registerCourse <*> occurrence <*> users
|
|
|
|
formResultModal usersToEnlist (CExamR tid ssh csh examn EUsersR) $ processUsers eEnt
|
|
|
|
let heading = prependCourseTitle tid ssh csh MsgExamParticipantsRegisterHeading
|
|
|
|
siteLayoutMsg heading $ do
|
|
setTitleI heading
|
|
wrapForm formWgt def
|
|
{ formEncoding
|
|
, formAction = Just . SomeRoute $ CExamR tid ssh csh examn EAddUserR
|
|
}
|
|
where
|
|
processUsers :: Entity Exam -> (UTCTime, Bool, Maybe ExamOccurrenceId, Set (Either UserEmail UserId)) -> WriterT [Message] Handler ()
|
|
processUsers (Entity eid Exam{..}) (deadline, registerCourse, occId, users) = do
|
|
let (emails,uids) = partitionEithers $ Set.toList users
|
|
AddRecipientsResult{..} <- lift . runDBJobs $ do
|
|
-- send Invitation eMails to unkown users
|
|
sinkInvitationsF examRegistrationInvitationConfig [(mail,eid,(InvDBDataExamRegistration occId deadline registerCourse, InvTokenDataExamRegistration)) | mail <- emails]
|
|
-- register known users
|
|
execWriterT $ mapM (registerUser examCourse eid registerCourse occId) uids
|
|
|
|
unless (null emails) $
|
|
tell . pure <=< messageI Success . MsgExamParticipantsInvited $ length emails
|
|
|
|
unless (null aurSuccess) $
|
|
tell . pure <=< messageI Success . MsgExamRegistrationParticipantsRegistered $ length aurSuccess
|
|
|
|
unless (null aurNoCourseRegistration) $ do
|
|
let modalTrigger = [whamlet|_{MsgExamRegistrationNotRegisteredWithoutCourse (length aurNoCourseRegistration)}|]
|
|
modalContent = $(widgetFile "messages/examRegistrationInvitationNotRegisteredWithoutCourse")
|
|
tell . pure <=< messageWidget Error $ msgModal modalTrigger (Right modalContent)
|
|
|
|
unless (null aurSuccessCourse) $
|
|
tell . pure <=< messageI Success . MsgExamRegistrationAndCourseParticipantsRegistered $ length aurSuccessCourse
|
|
|
|
registerUser :: CourseId -> ExamId -> Bool -> Maybe ExamOccurrenceId -> UserId -> WriterT AddRecipientsResult (YesodJobDB UniWorX) ()
|
|
registerUser cid eid registerCourse occId uid = exceptT tell tell $ do
|
|
User{..} <- lift . lift $ getJust uid
|
|
now <- liftIO getCurrentTime
|
|
|
|
let
|
|
examRegister :: YesodJobDB UniWorX ()
|
|
examRegister = do
|
|
insert_ $ ExamRegistration eid uid occId now
|
|
audit $ TransactionExamRegister eid uid
|
|
|
|
whenM (lift . lift . existsBy $ UniqueExamRegistration eid uid) $
|
|
throwError $ mempty { aurAlreadyRegistered = pure userEmail }
|
|
|
|
whenM (lift . lift $ exists [ CourseParticipantCourse ==. cid, CourseParticipantUser ==. uid, CourseParticipantState ==. CourseParticipantActive ]) $ do
|
|
lift $ lift examRegister
|
|
throwError $ mempty { aurSuccess = pure userEmail }
|
|
|
|
unless registerCourse $
|
|
throwError $ mempty { aurNoCourseRegistration = pure userEmail }
|
|
|
|
lift . lift . hoist lift $ guardAuthResult =<< evalAccessDB (CourseR tid ssh csh CAddUserR) True
|
|
|
|
|
|
lift . lift . void $ upsert
|
|
CourseParticipant
|
|
{ courseParticipantCourse = cid
|
|
, courseParticipantUser = uid
|
|
, courseParticipantRegistration = now
|
|
, courseParticipantAllocated = Nothing
|
|
, courseParticipantState = CourseParticipantActive
|
|
, ..
|
|
}
|
|
[ CourseParticipantRegistration =. now
|
|
, CourseParticipantAllocated =. Nothing
|
|
, CourseParticipantState =. CourseParticipantActive
|
|
]
|
|
lift . lift . audit $ TransactionCourseParticipantEdit cid uid
|
|
lift . lift . queueDBJob . JobQueueNotification $ NotificationCourseRegistered uid cid
|
|
lift $ lift examRegister
|
|
|
|
return $ mempty { aurSuccessCourse = pure userEmail }
|
|
|
|
|