chore(occurrences): add datatype LessonTime for dealing timetable intervals
This commit is contained in:
parent
a262921a7d
commit
384c39b9ec
@ -436,7 +436,7 @@ mkLmsTable :: ( Functor h, ToSortable h
|
|||||||
mkLmsTable isAdmin (Entity qid quali) acts cols psValidator = do
|
mkLmsTable isAdmin (Entity qid quali) acts cols psValidator = do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
-- lookup all companies
|
-- lookup all companies
|
||||||
cmpMap <- memcachedBy (Just . Right $ 15 * diffMinute) ("CompanyDictionary"::Text) $ do
|
cmpMap <- memcachedBy (Just . Right $ 30 * diffMinute) ("CompanyDictionary"::Text) $ do
|
||||||
cmps <- selectList [] [] -- [Asc CompanyShorthand]
|
cmps <- selectList [] [] -- [Asc CompanyShorthand]
|
||||||
return $ Map.fromList $ fmap (\Entity{..} -> (entityKey, entityVal)) cmps
|
return $ Map.fromList $ fmap (\Entity{..} -> (entityKey, entityVal)) cmps
|
||||||
let
|
let
|
||||||
|
|||||||
@ -363,7 +363,7 @@ mkQualificationTable isAdmin (Entity qid quali) acts cols psValidator = do
|
|||||||
svs <- getSupervisees
|
svs <- getSupervisees
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
-- lookup all companies
|
-- lookup all companies
|
||||||
cmpMap <- memcachedBy (Just . Right $ 15 * diffMinute) ("CompanyDictionary"::Text) $ do
|
cmpMap <- memcachedBy (Just . Right $ 30 * diffMinute) ("CompanyDictionary"::Text) $ do
|
||||||
cmps <- selectList [] [] -- [Asc CompanyShorthand]
|
cmps <- selectList [] [] -- [Asc CompanyShorthand]
|
||||||
return $ Map.fromList $ fmap (\Entity{..} -> (entityKey, entityVal)) cmps
|
return $ Map.fromList $ fmap (\Entity{..} -> (entityKey, entityVal)) cmps
|
||||||
let
|
let
|
||||||
|
|||||||
@ -81,14 +81,14 @@ getDayTutorials ssh dlimit@(dstart, dend )
|
|||||||
E.where_ $ crs E.^. CourseSchool E.==. E.val ssh
|
E.where_ $ crs E.^. CourseSchool E.==. E.val ssh
|
||||||
E.&&. trm E.^. TermStart E.<=. E.val dend
|
E.&&. trm E.^. TermStart E.<=. E.val dend
|
||||||
E.&&. trm E.^. TermEnd E.>=. E.val dstart
|
E.&&. trm E.^. TermEnd E.>=. E.val dstart
|
||||||
return (trm, tut)
|
return (trm, tut, E.just (tut E.^. TutorialTime) @>. E.jsonbVal (occurrenceDayValue dstart))
|
||||||
$logErrorS "memcached" $ "***DEBUG*****CACHE*****" <> tshow (ssh,dlimit) <> "***************" -- DEBUG ONLY
|
|
||||||
return $ mapMaybe checkCandidate candidates
|
return $ mapMaybe checkCandidate candidates
|
||||||
where
|
where
|
||||||
period = Set.fromAscList [dstart..dend]
|
period = Set.fromAscList [dstart..dend]
|
||||||
|
|
||||||
checkCandidate (Entity{entityVal=trm}, Entity{entityKey=tutId, entityVal=Tutorial{tutorialTime=JSONB occ}})
|
checkCandidate (_, Entity{entityKey=tutId}, E.unValue -> True) = Just tutId -- common case
|
||||||
| not $ Set.null $ Set.intersection period $ occurrencesCompute trm occ
|
checkCandidate (Entity{entityVal=trm}, Entity{entityKey=tutId, entityVal=Tutorial{tutorialTime=JSONB occ}},_)
|
||||||
|
| not $ Set.null $ Set.intersection period $ occurrencesCompute' trm occ
|
||||||
= Just tutId
|
= Just tutId
|
||||||
| otherwise
|
| otherwise
|
||||||
= Nothing
|
= Nothing
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import qualified Database.Esqueleto.Utils as E
|
|||||||
import System.FilePath (normalise, makeValid)
|
import System.FilePath (normalise, makeValid)
|
||||||
import Data.List (dropWhileEnd)
|
import Data.List (dropWhileEnd)
|
||||||
|
|
||||||
|
{-# ANN module ("HLint: ignore Redundant bracket" :: String) #-}
|
||||||
|
|
||||||
|
|
||||||
data SourceFilesException
|
data SourceFilesException
|
||||||
|
|||||||
@ -3,8 +3,10 @@
|
|||||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
module Handler.Utils.Occurrences
|
module Handler.Utils.Occurrences
|
||||||
( occurrencesWidget
|
( LessonTime(..)
|
||||||
, occurrencesCompute
|
, occurringLessons
|
||||||
|
, occurrencesWidget
|
||||||
|
, occurrencesCompute, occurrencesCompute'
|
||||||
, occurrencesBounds
|
, occurrencesBounds
|
||||||
, occurrencesAddBusinessDays
|
, occurrencesAddBusinessDays
|
||||||
) where
|
) where
|
||||||
@ -19,6 +21,52 @@ import Utils.Occurrences
|
|||||||
import Handler.Utils.DateTime
|
import Handler.Utils.DateTime
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----------------
|
||||||
|
-- LessonTime --
|
||||||
|
----------------
|
||||||
|
--
|
||||||
|
-- Model time intervals to compute lecture/tutorial lessons more intuitively
|
||||||
|
--
|
||||||
|
|
||||||
|
data LessonTime = LessonTime { lessonStart, lessonEnd :: LocalTime }
|
||||||
|
deriving (Eq, Ord, Read, Show, Generic) -- BEWARE: Ord instance might not be intuitive, but needed for Set
|
||||||
|
|
||||||
|
occurringLessons :: Term -> Occurrences -> Set LessonTime
|
||||||
|
occurringLessons t Occurrences{..} = Set.union exceptOcc $ Set.filter isExcept scheduledLessons
|
||||||
|
where
|
||||||
|
scheduledLessons = occurrenceScheduleToLessons t `foldMap` occurrencesScheduled
|
||||||
|
(exceptOcc, exceptNo) = occurrenceExceptionToLessons occurrencesExceptions
|
||||||
|
isExcept LessonTime{lessonStart} = Set.member lessonStart exceptNo
|
||||||
|
|
||||||
|
occurrenceScheduleToLessons :: Term -> OccurrenceSchedule -> Set LessonTime
|
||||||
|
occurrenceScheduleToLessons Term{..} =
|
||||||
|
let setHolidays = Set.fromList termHolidays
|
||||||
|
in \ScheduleWeekly{..} ->
|
||||||
|
let occDays = daysOfWeekBetween (termLectureStart, termLectureEnd) scheduleDayOfWeek \\ setHolidays
|
||||||
|
toLesson d = LessonTime { lessonStart = LocalTime d scheduleStart
|
||||||
|
, lessonEnd = LocalTime d scheduleEnd
|
||||||
|
}
|
||||||
|
in Set.map toLesson occDays
|
||||||
|
|
||||||
|
occurrenceExceptionToLessons :: Set OccurrenceException -> (Set LessonTime, Set LocalTime)
|
||||||
|
occurrenceExceptionToLessons = Set.foldr aux mempty
|
||||||
|
where
|
||||||
|
aux ExceptOccur{..} (oc,no) =
|
||||||
|
let t = LessonTime { lessonStart = LocalTime exceptDay exceptStart
|
||||||
|
, lessonEnd = LocalTime exceptDay exceptEnd
|
||||||
|
}
|
||||||
|
in (Set.insert t oc,no)
|
||||||
|
aux ExceptNoOccur{..} (oc,no) =
|
||||||
|
(oc, Set.insert exceptTime no)
|
||||||
|
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
-- Occurrences --
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
|
||||||
occurrencesWidget :: JSONB Occurrences -> Widget
|
occurrencesWidget :: JSONB Occurrences -> Widget
|
||||||
occurrencesWidget (normalizeOccurrences . unJSONB -> Occurrences{..}) = do
|
occurrencesWidget (normalizeOccurrences . unJSONB -> Occurrences{..}) = do
|
||||||
let occurrencesScheduled' = flip map (Set.toList occurrencesScheduled) $ \case
|
let occurrencesScheduled' = flip map (Set.toList occurrencesScheduled) $ \case
|
||||||
@ -36,7 +84,12 @@ occurrencesWidget (normalizeOccurrences . unJSONB -> Occurrences{..}) = do
|
|||||||
$(widgetFile "widgets/occurrence/cell/except-no-occur")
|
$(widgetFile "widgets/occurrence/cell/except-no-occur")
|
||||||
$(widgetFile "widgets/occurrence/cell")
|
$(widgetFile "widgets/occurrence/cell")
|
||||||
|
|
||||||
-- | Get all occurrences during a term, excluding term holidays from the regular schedule, but not from exceptins
|
-- | More precise verison of `occurrencesCompute`, which accounts for TimeOfDay as well
|
||||||
|
occurrencesCompute' :: Term -> Occurrences -> Set Day
|
||||||
|
occurrencesCompute' trm occ = Set.map (localDay . lessonStart) $ occurringLessons trm occ
|
||||||
|
|
||||||
|
-- | Get all occurrences during a term, excluding term holidays from the regular schedule, but not from exceptions
|
||||||
|
-- Beware: code currently ignores TimeOfDay, see Model.Types.DateTime.LessonTime for a start to address this if needed
|
||||||
occurrencesCompute :: Term -> Occurrences -> Set Day
|
occurrencesCompute :: Term -> Occurrences -> Set Day
|
||||||
occurrencesCompute Term{..} Occurrences{..} = ((scdDays \\ Set.fromList termHolidays) <> plsDays) \\ excDays
|
occurrencesCompute Term{..} Occurrences{..} = ((scdDays \\ Set.fromList termHolidays) <> plsDays) \\ excDays
|
||||||
where
|
where
|
||||||
|
|||||||
@ -48,6 +48,7 @@ import System.IO.Unsafe
|
|||||||
|
|
||||||
import Data.Typeable (eqT)
|
import Data.Typeable (eqT)
|
||||||
|
|
||||||
|
{-# ANN module ("HLint: ignore Redundant bracket" :: String) #-}
|
||||||
|
|
||||||
sinkFileDB :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnliftIO m, BackendCompatible SqlBackend (YesodPersistBackend UniWorX), YesodPersist UniWorX)
|
sinkFileDB :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnliftIO m, BackendCompatible SqlBackend (YesodPersistBackend UniWorX), YesodPersist UniWorX)
|
||||||
=> Bool -- ^ Replace? Use only in serializable transaction
|
=> Bool -- ^ Replace? Use only in serializable transaction
|
||||||
@ -63,9 +64,9 @@ sinkFileDB doReplace fileContentContent = do
|
|||||||
observeSunkChunk StorageDB $ olength fileContentChunkContent
|
observeSunkChunk StorageDB $ olength fileContentChunkContent
|
||||||
|
|
||||||
tellM $ Set.singleton <$> insert FileChunkLock{ fileChunkLockHash = fileContentChunkHash, .. }
|
tellM $ Set.singleton <$> insert FileChunkLock{ fileChunkLockHash = fileContentChunkHash, .. }
|
||||||
|
|
||||||
existsChunk <- lift $ exists [FileContentChunkHash ==. fileContentChunkHash]
|
existsChunk <- lift $ exists [FileContentChunkHash ==. fileContentChunkHash]
|
||||||
|
|
||||||
let setContentBased = updateWhere [FileContentChunkHash ==. fileContentChunkHash] [FileContentChunkContentBased =. fileContentChunkContentBased]
|
let setContentBased = updateWhere [FileContentChunkHash ==. fileContentChunkHash] [FileContentChunkContentBased =. fileContentChunkContentBased]
|
||||||
if | existsChunk -> lift setContentBased
|
if | existsChunk -> lift setContentBased
|
||||||
| otherwise -> lift . handleIfSql isUniqueConstraintViolation (const setContentBased) $
|
| otherwise -> lift . handleIfSql isUniqueConstraintViolation (const setContentBased) $
|
||||||
@ -98,7 +99,7 @@ sinkFileDB doReplace fileContentContent = do
|
|||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
deleteWhere [ FileContentEntryHash ==. fileContentHash ]
|
deleteWhere [ FileContentEntryHash ==. fileContentHash ]
|
||||||
insertEntries
|
insertEntries
|
||||||
|
|
||||||
|
|
||||||
return fileContentHash
|
return fileContentHash
|
||||||
where fileContentChunkContentBased = True
|
where fileContentChunkContentBased = True
|
||||||
@ -163,18 +164,18 @@ sinkMinio content = do
|
|||||||
, Minio.dstObject = dstName
|
, Minio.dstObject = dstName
|
||||||
}
|
}
|
||||||
uploadExists <- handleIf minioIsDoesNotExist (const $ return False) $ True <$ Minio.statObject uploadBucket dstName Minio.defaultGetObjectOptions
|
uploadExists <- handleIf minioIsDoesNotExist (const $ return False) $ True <$ Minio.statObject uploadBucket dstName Minio.defaultGetObjectOptions
|
||||||
unless uploadExists $
|
unless uploadExists $
|
||||||
Minio.copyObject copyDst copySrc
|
Minio.copyObject copyDst copySrc
|
||||||
release removeObject
|
release removeObject
|
||||||
return $ _sinkMinioRet # contentHash
|
return $ _sinkMinioRet # contentHash
|
||||||
|
|
||||||
sinkFileMinio :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnliftIO m)
|
sinkFileMinio :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnliftIO m)
|
||||||
=> ConduitT () ByteString m ()
|
=> ConduitT () ByteString m ()
|
||||||
-> MaybeT m FileContentReference
|
-> MaybeT m FileContentReference
|
||||||
-- ^ Cannot deal with zero length uploads
|
-- ^ Cannot deal with zero length uploads
|
||||||
sinkFileMinio = sinkMinio @FileContentReference
|
sinkFileMinio = sinkMinio @FileContentReference
|
||||||
|
|
||||||
|
|
||||||
sinkFiles :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnliftIO m, BackendCompatible SqlBackend (YesodPersistBackend UniWorX), YesodPersist UniWorX) => ConduitT (File (SqlPersistT m)) FileReference (SqlPersistT m) ()
|
sinkFiles :: (MonadCatch m, MonadHandler m, HandlerSite m ~ UniWorX, MonadUnliftIO m, BackendCompatible SqlBackend (YesodPersistBackend UniWorX), YesodPersist UniWorX) => ConduitT (File (SqlPersistT m)) FileReference (SqlPersistT m) ()
|
||||||
sinkFiles = C.mapM sinkFile
|
sinkFiles = C.mapM sinkFile
|
||||||
|
|
||||||
|
|||||||
@ -15,4 +15,5 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
|
|||||||
$forall (Entity _ usr) <- tutors
|
$forall (Entity _ usr) <- tutors
|
||||||
<li>
|
<li>
|
||||||
^{userEmailWidget usr}
|
^{userEmailWidget usr}
|
||||||
^{participantTable}
|
<section>
|
||||||
|
^{participantTable}
|
||||||
|
|||||||
Reference in New Issue
Block a user