Convenience for new exercise sheets implemented.

This commit is contained in:
SJost 2018-09-18 10:45:44 +02:00
parent c684692cc2
commit eed3cf51ae
5 changed files with 87 additions and 27 deletions

View File

@ -145,7 +145,7 @@ pattern CSubmissionR tid ssh csh shn cid ptn
-- Menus and Favourites -- Menus and Favourites
data MenuItem = MenuItem data MenuItem = MenuItem
{ menuItemLabel :: Text { menuItemLabel :: Text
, menuItemIcon :: Maybe Text , menuItemIcon :: Maybe Text
, menuItemRoute :: Route UniWorX , menuItemRoute :: Route UniWorX
, menuItemAccessCallback' :: Handler Bool -- Check whether action is shown in ADDITION to authorization (which is always checked) , menuItemAccessCallback' :: Handler Bool -- Check whether action is shown in ADDITION to authorization (which is always checked)
} }

View File

@ -349,7 +349,32 @@ getSFileR tid ssh csh shn typ title = do
getSheetNewR :: TermId -> SchoolId -> CourseShorthand -> Handler Html getSheetNewR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
getSheetNewR tid ssh csh = do getSheetNewR tid ssh csh = do
let template = Nothing -- TODO: provide convenience by interpolating name/nr/dates+7days lastSheets <- runDB $ E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse
E.where_ $ course E.^. CourseTerm E.==. E.val tid
E.&&. course E.^. CourseSchool E.==. E.val ssh
E.&&. course E.^. CourseShorthand E.==. E.val csh
E.orderBy [E.desc (sheet E.^. SheetActiveFrom)]
E.limit 1
return sheet
let template = case lastSheets of
((Entity {entityVal=Sheet{..}}):_) -> Just $ SheetForm
{ sfName = stepTextCounterCI sheetName
, sfDescription = sheetDescription
, sfType = sheetType
, sfGrouping = sheetGrouping
, sfMarkingText = sheetMarkingText
, sfVisibleFrom = addOneWeek <$> sheetVisibleFrom
, sfActiveFrom = addOneWeek sheetActiveFrom
, sfActiveTo = addOneWeek sheetActiveTo
, sfSheetF = Nothing
, sfHintFrom = addOneWeek <$> sheetHintFrom
, sfHintF = Nothing
, sfSolutionFrom = addOneWeek <$> sheetSolutionFrom
, sfSolutionF = Nothing
, sfMarkingF = Nothing
}
_other -> Nothing
let action newSheet = -- More specific error message for new sheet could go here, if insertUnique returns Nothing let action newSheet = -- More specific error message for new sheet could go here, if insertUnique returns Nothing
insertUnique $ newSheet insertUnique $ newSheet
handleSheetEdit tid ssh csh Nothing template action handleSheetEdit tid ssh csh Nothing template action

View File

@ -3,7 +3,7 @@
{-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, UndecidableInstances #-} {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE TypeFamilies, FlexibleContexts, ConstraintKinds #-} {-# LANGUAGE TypeFamilies, FlexibleContexts, ConstraintKinds #-}
{-# LANGUAGE QuasiQuotes, TemplateHaskell #-} {-# LANGUAGE QuasiQuotes, TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ViewPatterns #-}
@ -16,7 +16,7 @@ module Utils
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
-- import Data.Double.Conversion.Text -- faster implementation for textPercent? -- import Data.Double.Conversion.Text -- faster implementation for textPercent?
import Data.Foldable as Fold import Data.Foldable as Fold hiding (length)
import Data.CaseInsensitive (CI) import Data.CaseInsensitive (CI)
import qualified Data.CaseInsensitive as CI import qualified Data.CaseInsensitive as CI
@ -28,7 +28,8 @@ import Utils.PathPiece as Utils
import Text.Blaze (Markup, ToMarkup) import Text.Blaze (Markup, ToMarkup)
import Data.Text (dropWhileEnd) import Data.Char (isDigit)
import Data.Text (dropWhileEnd, takeWhileEnd, justifyRight)
import Numeric (showFFloat) import Numeric (showFFloat)
import Control.Lens import Control.Lens
@ -50,6 +51,8 @@ import Instances.TH.Lift ()
import Text.Shakespeare.Text (st) import Text.Shakespeare.Text (st)
----------- -----------
-- Yesod -- -- Yesod --
----------- -----------
@ -85,6 +88,7 @@ unsupportedAuthPredicate = do
|] |]
--------------------- ---------------------
-- Text and String -- -- Text and String --
--------------------- ---------------------
@ -100,6 +104,9 @@ tickmarkT = tickmark
text2Html :: Text -> Html text2Html :: Text -> Html
text2Html = toHtml -- prevents ambiguous types text2Html = toHtml -- prevents ambiguous types
liftCI :: (Text -> Text) -> (CI Text) -> (CI Text)
liftCI f ci = CI.mk $ f $ CI.original ci
toWgt :: (ToMarkup a, MonadBaseControl IO m, MonadThrow m, MonadIO m) => toWgt :: (ToMarkup a, MonadBaseControl IO m, MonadThrow m, MonadIO m) =>
a -> WidgetT site m () a -> WidgetT site m ()
toWgt = toWidget . toHtml toWgt = toWidget . toHtml
@ -121,15 +128,14 @@ display2widget :: (MonadBaseControl IO m, MonadThrow m, MonadIO m, DisplayAble
a -> WidgetT site m () a -> WidgetT site m ()
display2widget = text2widget . display display2widget = text2widget . display
withFragment :: ( Monad m withFragment :: Monad m => MForm m (a, WidgetT site IO ()) -> Markup -> MForm m (a, WidgetT site IO ())
) => MForm m (a, WidgetT site IO ()) -> Markup -> MForm m (a, WidgetT site IO ())
withFragment form html = (flip fmap) form $ \(x, widget) -> (x, toWidget html >> widget) withFragment form html = (flip fmap) form $ \(x, widget) -> (x, toWidget html >> widget)
-- Convert anything to Text, and I don't care how -- Types that can be converted to Text for direct displayed to User! (Show for debugging, Display for Production)
class DisplayAble a where class DisplayAble a where
display :: a -> Text display :: a -> Text
-- Default definitions for type belonging to Show (allows empty instanc declarations) -- Default definitions for types belonging to Show (allows empty instance declarations)
default display :: Show a => a -> Text default display :: Show a => a -> Text
display = pack . show display = pack . show
@ -139,10 +145,6 @@ instance DisplayAble Text where
instance DisplayAble String where instance DisplayAble String where
display = pack display = pack
instance DisplayAble a => DisplayAble (Maybe a) where
display Nothing = ""
display (Just x) = display x
instance DisplayAble Int instance DisplayAble Int
instance DisplayAble Int64 instance DisplayAble Int64
instance DisplayAble Integer instance DisplayAble Integer
@ -156,19 +158,21 @@ instance DisplayAble Rational where
rat2float :: Rational -> Double rat2float :: Rational -> Double
rat2float = fromRational rat2float = fromRational
instance DisplayAble a => DisplayAble (Maybe a) where
display Nothing = ""
display (Just x) = display x
instance DisplayAble a => DisplayAble (E.Value a) where instance DisplayAble a => DisplayAble (E.Value a) where
display = display . E.unValue display = display . E.unValue
instance DisplayAble a => DisplayAble (CI a) where instance DisplayAble a => DisplayAble (CI a) where
display = display . CI.original display = display . CI.original
{- We do not want DisplayAble for every Show-Class, we want to check that it looks good and explicitely add Instances only, {- We do not want DisplayAble for every Show-Class:
for example, UTCTime values were shown without proper rendering! We want to explicitly verify that the resulting text can be displayed to the User!
For example: UTCTime values were shown without proper format rendering!
-- The easy way out of UndecidableInstances (TypeFamilies would have been proper, but are much more complicated) instance {-# OVERLAPPABLE #-} Show a => DisplayAble a where -- The easy way out of UndecidableInstances (TypeFamilies would have been proper, but are much more complicated)
instance {-# OVERLAPPABLE #-} Show a => DisplayAble a where
display = pack . show display = pack . show
-} -}
textPercent :: Double -> Text -- slow, maybe use Data.Double.Conversion.Text.toFixed instead? textPercent :: Double -> Text -- slow, maybe use Data.Double.Conversion.Text.toFixed instead?
@ -180,6 +184,22 @@ textPercent x = lz <> (pack $ show rx) <> "%"
rx = fromIntegral (round' $ 1000.0*x) / 10.0 rx = fromIntegral (round' $ 1000.0*x) / 10.0
lz = if rx < 10.0 then "0" else "" lz = if rx < 10.0 then "0" else ""
stepTextCounterCI :: CI Text -> CI Text -- find and increment rightmost-number, preserving leading zeroes
stepTextCounterCI = liftCI stepTextCounter
stepTextCounter :: Text -> Text -- find and increment rightmost-number, preserving leading zeroes
stepTextCounter text
| (Just i) <- readMay number =
let iplus1 = tshow (succ i :: Int)
zeroip = justifyRight (length number) '0' iplus1
in prefix <> zeroip <> suffix
| otherwise = text
where -- no splitWhile nor findEnd in Data.Text
suffix = takeWhileEnd (not . isDigit) text
number = takeWhileEnd isDigit $ dropWhileEnd (not . isDigit) text
prefix = dropWhileEnd isDigit $ dropWhileEnd (not . isDigit) text
------------ ------------
-- Tuples -- -- Tuples --
@ -197,6 +217,7 @@ trd3 (_,_,z) = z
-- snd3 = $(projNI 3 2) -- snd3 = $(projNI 3 2)
----------- -----------
-- Lists -- -- Lists --
----------- -----------
@ -229,6 +250,8 @@ mergeAttrs = mergeAttrs' `on` sort
mergeAttrs' [] xs2 = xs2 mergeAttrs' [] xs2 = xs2
mergeAttrs' xs1 [] = xs1 mergeAttrs' xs1 [] = xs1
---------- ----------
-- Maps -- -- Maps --
---------- ----------
@ -248,6 +271,8 @@ partMap = Map.fromListWith mappend
invertMap :: (Ord k, Ord v) => Map k v -> Map v (Set k) invertMap :: (Ord k, Ord v) => Map k v -> Map v (Set k)
invertMap = groupMap . map swap . Map.toList invertMap = groupMap . map swap . Map.toList
----------- -----------
-- Maybe -- -- Maybe --
----------- -----------
@ -296,6 +321,8 @@ instance Ord a => Ord (NTop (Maybe a)) where
compare _ (NTop Nothing) = LT compare _ (NTop Nothing) = LT
compare (NTop (Just x)) (NTop (Just y)) = compare x y compare (NTop (Just x)) (NTop (Just y)) = compare x y
--------------- ---------------
-- Exception -- -- Exception --
--------------- ---------------
@ -325,6 +352,7 @@ catchIfMExceptT :: (MonadCatch m, Exception e) => (e -> m e') -> (e -> Bool) ->
catchIfMExceptT err p act = catchIf p (lift act) (throwE <=< lift . err) catchIfMExceptT err p act = catchIf p (lift act) (throwE <=< lift . err)
------------ ------------
-- Monads -- -- Monads --
------------ ------------

View File

@ -10,6 +10,7 @@ module Utils.DateTime
( timeLocaleMap ( timeLocaleMap
, TimeLocale(..) , TimeLocale(..)
, currentYear , currentYear
, addOneWeek
, module Data.Time.Zones , module Data.Time.Zones
, module Data.Time.Zones.TH , module Data.Time.Zones.TH
) where ) where
@ -18,6 +19,7 @@ import ClassyPrelude.Yesod hiding (lift)
import System.Locale.Read import System.Locale.Read
import Data.Time (TimeZone(..), TimeLocale(..)) import Data.Time (TimeZone(..), TimeLocale(..))
import Data.Time.Clock (addUTCTime,nominalDay)
import Data.Time.Zones (TZ) import Data.Time.Zones (TZ)
import Data.Time.Zones.TH (includeSystemTZ) import Data.Time.Zones.TH (includeSystemTZ)
@ -28,6 +30,11 @@ import Instances.TH.Lift ()
deriving instance Lift TimeZone deriving instance Lift TimeZone
deriving instance Lift TimeLocale deriving instance Lift TimeLocale
addOneWeek :: UTCTime -> UTCTime
addOneWeek = addUTCTime (7 * nominalDay) --better use nominalWeek
-- $(timeLocaleMap _) :: [Lang] -> TimeLocale -- $(timeLocaleMap _) :: [Lang] -> TimeLocale
timeLocaleMap :: [(Lang, String)] -- ^ Languages and matching locales, first is taken as default timeLocaleMap :: [(Lang, String)] -- ^ Languages and matching locales, first is taken as default
-> ExpQ -> ExpQ

View File

@ -38,14 +38,14 @@ nullaryToPathPiece nullaryType manglers = do
splitCamel :: Text -> [Text] splitCamel :: Text -> [Text]
splitCamel = map Text.pack . reverse . helper (error "hasChange undefined at start of string") [] "" . Text.unpack splitCamel = map Text.pack . reverse . helper (error "hasChange undefined at start of string") [] "" . Text.unpack
where where
helper hadChange words thisWord [] = reverse thisWord : words helper _hadChange items thisWord [] = reverse thisWord : items
helper hadChange words [] (c:cs) = helper True words [c] cs helper _hadChange items [] (c:cs) = helper True items [c] cs
helper hadChange words ws@(w:ws') (c:cs) helper hadChange items ws@(w:ws') (c:cs)
| sameCategory w c | sameCategory w c
, null ws' = helper False words (c:ws) cs , null ws' = helper False items (c:ws) cs
| sameCategory w c = helper hadChange words (c:ws) cs | sameCategory w c = helper hadChange items (c:ws) cs
| null ws' = helper True words (c:ws) cs | null ws' = helper True items (c:ws) cs
| not hadChange = helper True (reverse ws':words) [c,w] cs | not hadChange = helper True (reverse ws':items) [c,w] cs
| otherwise = helper True (reverse ws:words) [c] cs | otherwise = helper True (reverse ws :items) [c] cs
sameCategory = (==) `on` Char.generalCategory sameCategory = (==) `on` Char.generalCategory