weekdaze-0.0.0.1: src-lib/WeekDaze/OutputConfiguration/Style.hs
{-# LANGUAGE CPP #-}
{-
Copyright (C) 2013-2015 Dr. Alistair Ward
This file is part of WeekDaze.
WeekDaze is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
WeekDaze is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with WeekDaze. If not, see <http://www.gnu.org/licenses/>.
-}
{- |
[@AUTHOR@] Dr. Alistair Ward
[@DESCRIPTION@] Defines the style of the output.
-}
module WeekDaze.OutputConfiguration.Style(
-- * Types
-- ** Type-synonyms
DisplayRuntimeInformation,
DisplaySupplementaryInformation,
-- GenerateLessonColour,
-- ** Data-types
Style(
-- MkStyle,
getDisplayViews,
getMaybeCSSURL,
getMergeDuplicateTimeslots,
getDisplayAxisLabels,
getDisplayRuntimeInformation,
getDisplaySupplementaryInformation,
getWeekend,
getMaybeGenerateLessonColour
),
-- * Constants
-- tag,
-- byDayTag,
-- byTimeslotTag,
-- cssURLTag,
-- displayAxisLabelsTag,
displayViewsTag,
displayRuntimeInformationTag,
-- displaySupplementaryInformationTag,
-- mergeDuplicateTimeslotsTag,
-- perspectiveTag,
weekendTag,
-- minimumContrastRatioTag,
-- minimumContrastRatioDefault,
-- * Functions
-- ** Constructor
mkStyle
) where
import qualified Control.DeepSeq
import qualified Data.Default
import qualified Data.Maybe
import qualified Data.Set
import qualified Text.XML.HXT.Arrow.Pickle as HXT
import qualified ToolShed.SelfValidate
import qualified WeekDaze.Model.GenerateLessonColourFrom as Model.GenerateLessonColourFrom
import qualified WeekDaze.OutputConfiguration.View as OutputConfiguration.View
import qualified WeekDaze.Temporal.Day as Temporal.Day
import qualified WeekDaze.Temporal.TimeAxes as Temporal.TimeAxes
import WeekDaze.Enhanced.EnhancedBool()
#ifdef USE_HDBC
import qualified Database.HDBC
import qualified Data.Convertible
import qualified WeekDaze.Database.Selector as Database.Selector
instance Fractional minimumContrastRatio => Database.Selector.Selector (Style minimumContrastRatio) where
fromDatabase connection projectIdSql = let
tableName :: Database.Selector.TableName
tableName = showString Database.Selector.tablePrefix tag
in do
styleRows <- Database.Selector.select connection [
-- displayViews,
cssURLTag,
showString mergeDuplicateTimeslotsTag byDayTag,
showString mergeDuplicateTimeslotsTag byTimeslotTag,
showString displayAxisLabelsTag byDayTag,
showString displayAxisLabelsTag byTimeslotTag,
displayRuntimeInformationTag,
displaySupplementaryInformationTag,
weekendTag,
Model.GenerateLessonColourFrom.tag,
minimumContrastRatioTag
] [tableName] [(Database.Selector.projectIdColumnName, projectIdSql)]
return {-to IO-monad-} $ case styleRows of
[] -> Data.Default.def
[styleRow] -> case styleRow of
[
-- displayViewsSql,
cssURLSql,
mergeDuplicateTimeslotsByDaySql,
mergeDuplicateTimeslotsByTimeslotSql,
displayAxisLabelsByDaySql,
displayAxisLabelsByTimeslotSql,
displayRuntimeInformationSql,
displaySupplementaryInformationSql,
weekendSql,
generateLessonColourFromSql,
minimumContrastRatioSql
] -> Data.Default.def {
-- getDisplayViews,
getMaybeCSSURL = Database.HDBC.fromSql cssURLSql,
getMergeDuplicateTimeslots = either (
error . showString "WeekDaze.OutputConfiguration.Style.fromDatabase:\tfailed to parse the value for " . shows (mergeDuplicateTimeslotsTag ++ byDayTag) . showString " read from the database; " . show
) id (
Database.HDBC.safeFromSql mergeDuplicateTimeslotsByDaySql
) `Temporal.TimeAxes.mkTimeAxes` either (
error . showString "WeekDaze.OutputConfiguration.Style.fromDatabase:\tfailed to parse the value for " . shows (mergeDuplicateTimeslotsTag ++ byTimeslotTag) . showString " read from the database; " . show
) id (
Database.HDBC.safeFromSql mergeDuplicateTimeslotsByTimeslotSql
),
getDisplayAxisLabels = either (
error . showString "WeekDaze.OutputConfiguration.Style.fromDatabase:\tfailed to parse the value for " . shows (displayAxisLabelsTag ++ byDayTag) . showString " read from the database; " . show
) id (
Database.HDBC.safeFromSql displayAxisLabelsByDaySql
) `Temporal.TimeAxes.mkTimeAxes` either (
error . showString "WeekDaze.OutputConfiguration.Style.fromDatabase:\tfailed to parse the value for " . shows (displayAxisLabelsTag ++ byTimeslotTag) . showString " read from the database; " . show
) id (
Database.HDBC.safeFromSql displayAxisLabelsByTimeslotSql
),
getDisplayRuntimeInformation = either (
error . showString "WeekDaze.OutputConfiguration.Style.fromDatabase:\tfailed to parse the value for " . shows displayRuntimeInformationTag . showString " read from the database; " . show
) id $ Database.HDBC.safeFromSql displayRuntimeInformationSql,
getDisplaySupplementaryInformation = either (
error . showString "WeekDaze.OutputConfiguration.Style.fromDatabase:\tfailed to parse the value for " . shows displaySupplementaryInformationTag . showString " read from the database; " . show
) id $ Database.HDBC.safeFromSql displaySupplementaryInformationSql,
getWeekend = Database.HDBC.fromSql weekendSql,
getMaybeGenerateLessonColour = flip (,) (
Data.Maybe.maybe minimumContrastRatioDefault realToFrac (
either (
error . showString "WeekDaze.OutputConfiguration.Style.fromDatabase:\tfailed to parse the value for " . shows minimumContrastRatioTag . showString " read from the database; " . show
) id (
Database.HDBC.safeFromSql minimumContrastRatioSql :: Data.Convertible.ConvertResult (Maybe Double)
)
)
) `fmap` either (
error . showString "WeekDaze.OutputConfiguration.Style.fromDatabase:\tfailed to parse the value for " . shows Model.GenerateLessonColourFrom.tag . showString " read from the database; " . show
) id (
Database.HDBC.safeFromSql generateLessonColourFromSql
)
}
_ -> error $ "WeekDaze.OutputConfiguration.Style.fromDatabase:\tunexpected number of columns=" ++ show (length styleRow) ++ " in row of table " ++ show tableName ++ "."
_ -> error $ "WeekDaze.OutputConfiguration.Style.fromDatabase:\tunexpected number of rows=" ++ show (length styleRows) ++ " selected from table " ++ show tableName ++ "."
#endif /* USE_HDBC */
-- | Used to qualify XML.
tag :: String
tag = "style"
-- | Used to qualify SQL.
byDayTag :: String
byDayTag = "ByDay"
-- | Used to qualify SQL.
byTimeslotTag :: String
byTimeslotTag = "ByTimeslot"
-- | Used to qualify XML.
displayViewsTag :: String
displayViewsTag = "displayViews"
-- | Used to qualify SQL & XML.
cssURLTag :: String
cssURLTag = "cssURL"
-- | Used to qualify SQL & XML.
mergeDuplicateTimeslotsTag :: String
mergeDuplicateTimeslotsTag = "mergeDuplicateTimeslots"
-- | Used to qualify SQL & XML.
displayAxisLabelsTag :: String
displayAxisLabelsTag = "displayAxisLabels"
-- | Used to qualify SQL & XML.
displayRuntimeInformationTag :: String
displayRuntimeInformationTag = "displayRuntimeInformation"
-- | Used to qualify SQL & XML.
displaySupplementaryInformationTag :: String
displaySupplementaryInformationTag = "displaySupplementaryInformation"
-- | Used to qualify XML.
perspectiveTag :: String
perspectiveTag = "perspective"
-- | Used to qualify SQL & XML.
weekendTag :: String
weekendTag = "weekend"
-- | Used to qualify SQL & XML.
minimumContrastRatioTag :: String
minimumContrastRatioTag = "minimumContrastRatio"
-- | Default value.
minimumContrastRatioDefault :: Fractional minimumContrastRatio => minimumContrastRatio
minimumContrastRatioDefault = recip 16
-- | Whether to apply a concept to each of the two Cartesian axes of a timetable.
type TimeAxes = Temporal.TimeAxes.TimeAxes Bool
-- | Whether to display the runtime-log in the output markup.
type DisplayRuntimeInformation = Bool
-- | Whether to display the runtime-log in the output markup.
type DisplaySupplementaryInformation = Bool
-- | The attribute from which to generate the colour of a /lesson/ & the minimum acceptable contrast-ratio wrt to its complementary background colour.
type GenerateLessonColour minimumContrastRatio = (Model.GenerateLessonColourFrom.GenerateLessonColourFrom, minimumContrastRatio) -- Pair.
-- | Defines the style in which the timetable is rendered.
data Style minimumContrastRatio = MkStyle {
getDisplayViews :: Data.Set.Set OutputConfiguration.View.View, -- ^ The /view/s from which to display the /timetable/.
getMaybeCSSURL :: Maybe String, -- ^ Optional URL to a Cascading Style-Sheet.
getMergeDuplicateTimeslots :: TimeAxes, -- ^ Options to merge duplicate /lesson/s between adjacent /day/s, & between consecutive /time-slot/s respectively, in the presentation of the solution.
getDisplayAxisLabels :: TimeAxes, -- ^ Options to display /day/ & /timeslot-id/ axis-labels respectively, in the presentation of the solution.
getDisplayRuntimeInformation :: DisplayRuntimeInformation, -- ^ Option to display both warnings & the runtime-log, in the presentation of the solution.
getDisplaySupplementaryInformation :: DisplaySupplementaryInformation, -- ^ Option to augment the output with supplementary information.
getWeekend :: Temporal.Day.Weekend, -- ^ The set of /day/s generally considered to constitute the weekend rather than work-days.
getMaybeGenerateLessonColour :: Maybe (GenerateLessonColour minimumContrastRatio) -- ^ Whether to generate the colour of a /lesson/ from one of its attributes or to delegate colouring to the CSS-file specified via 'getMaybeCSSURL'.
} deriving (Eq, Show)
instance (
Num minimumContrastRatio,
Ord minimumContrastRatio
) => ToolShed.SelfValidate.SelfValidator (Style minimumContrastRatio) where
getErrors MkStyle {
getDisplayViews = displayViews,
getMaybeGenerateLessonColour = maybeGenerateLessonColour
} = ToolShed.SelfValidate.extractErrors [
(
Data.Set.null displayViews,
"at least one view of the results must be specified; " ++ show displayViewsTag
), (
Data.Maybe.maybe False (
(
\minimumContrastRatio -> any ($ minimumContrastRatio) [(< 0), (> 1)]
) . snd
) maybeGenerateLessonColour,
show minimumContrastRatioTag ++ "' must be within the closed unit-interval '[0,1]'"
)
]
instance Data.Default.Default (Style minimumContrastRatio) where
def = MkStyle {
getDisplayViews = Data.Set.fromList OutputConfiguration.View.range,
getMaybeCSSURL = Nothing,
getMergeDuplicateTimeslots = Temporal.TimeAxes.mkTimeAxes True True,
getDisplayAxisLabels = Temporal.TimeAxes.mkTimeAxes True True,
getDisplayRuntimeInformation = True,
getDisplaySupplementaryInformation = True,
getWeekend = Data.Set.fromList [minBound, maxBound],
getMaybeGenerateLessonColour = Nothing
}
instance Control.DeepSeq.NFData minimumContrastRatio => Control.DeepSeq.NFData (Style minimumContrastRatio) where
rnf (MkStyle x0 x1 x2 x3 x4 x5 x6 x7) = Control.DeepSeq.rnf (x0, x1, x2, x3, x4, x5, x6, x7)
-- | Smart constructor.
mkStyle :: (
Num minimumContrastRatio,
Ord minimumContrastRatio
)
=> Data.Set.Set OutputConfiguration.View.View -- ^ The /view/s from which to display the /timetable/, when rendering as xhtml.
-> Maybe String -- ^ The optional URL to a CSS-file.
-> TimeAxes
-> TimeAxes
-> DisplayRuntimeInformation
-> DisplaySupplementaryInformation
-> Temporal.Day.Weekend -- ^ Those /day/s of the weekend, which may be visually distingished by CSS.
-> Maybe (Model.GenerateLessonColourFrom.GenerateLessonColourFrom, minimumContrastRatio)
-> Style minimumContrastRatio
mkStyle displayViews maybeCSSURL mergeDuplicateTimeslots displayAxisLabels displayRuntimeInformation displaySupplementaryInformation weekend maybeGenerateLessonColour
| ToolShed.SelfValidate.isValid style = style
| otherwise = error $ "WeekDaze.OutputConfiguration.Style.mkStyle:\t" ++ ToolShed.SelfValidate.getFirstError style ++ "."
where
style = MkStyle {
getDisplayViews = displayViews,
getMaybeCSSURL = maybeCSSURL,
getMergeDuplicateTimeslots = mergeDuplicateTimeslots,
getDisplayAxisLabels = displayAxisLabels,
getDisplayRuntimeInformation = displayRuntimeInformation,
getDisplaySupplementaryInformation = displaySupplementaryInformation,
getWeekend = weekend,
getMaybeGenerateLessonColour = maybeGenerateLessonColour
}
instance (
Fractional minimumContrastRatio,
HXT.XmlPickler minimumContrastRatio,
Ord minimumContrastRatio
) => HXT.XmlPickler (Style minimumContrastRatio) where
xpickle = HXT.xpDefault defaultStyle . HXT.xpElem tag . HXT.xpWrap (
\(a, b, c, d, e, f, g, h) -> mkStyle a b c d e f g h, -- Construct from a tuple.
\MkStyle {
getDisplayViews = displayViews,
getMaybeCSSURL = maybeCSSURL,
getMergeDuplicateTimeslots = mergeDuplicateTimeslots,
getDisplayAxisLabels = displayAxisLabels,
getDisplayRuntimeInformation = displayRuntimeInformation,
getDisplaySupplementaryInformation = displaySupplementaryInformation,
getWeekend = weekend,
getMaybeGenerateLessonColour = maybeGenerateLessonColour
} -> (displayViews, maybeCSSURL, mergeDuplicateTimeslots, displayAxisLabels, displayRuntimeInformation, displaySupplementaryInformation, weekend, maybeGenerateLessonColour) -- Deconstruct into a tuple.
) $ HXT.xp8Tuple (
HXT.xpDefault (getDisplayViews Data.Default.def) . HXT.xpElem displayViewsTag . HXT.xpWrap (
Data.Set.fromList,
Data.Set.toList
) . HXT.xpList1 {-can't be null-} $ HXT.xpElem perspectiveTag HXT.xpickle {-View-}
) (
HXT.xpOption $ HXT.xpTextAttr cssURLTag {-can't be null-}
) (
getMergeDuplicateTimeslots defaultStyle `HXT.xpDefault` HXT.xpElem mergeDuplicateTimeslotsTag HXT.xpickle {-TimeAxes-}
) (
getDisplayAxisLabels defaultStyle `HXT.xpDefault` HXT.xpElem displayAxisLabelsTag HXT.xpickle {-TimeAxes-}
) (
getDisplayRuntimeInformation defaultStyle `HXT.xpDefault` HXT.xpAttr displayRuntimeInformationTag HXT.xpickle {-Bool-}
) (
getDisplaySupplementaryInformation defaultStyle `HXT.xpDefault` HXT.xpAttr displaySupplementaryInformationTag HXT.xpickle {-Bool-}
) (
HXT.xpDefault (getWeekend defaultStyle) . HXT.xpElem weekendTag . HXT.xpWrap (
Data.Set.fromList, -- Construct from a List.
Data.Set.toList -- Deconstruct to a List.
) $ HXT.xpList {-can be null-} HXT.xpickle {-Day-}
) (
HXT.xpOption . HXT.xpElem "generateLessonColour" $ HXT.xpickle {-GenerateLessonColourFrom-} `HXT.xpPair` HXT.xpDefault minimumContrastRatioDefault (
HXT.xpAttr minimumContrastRatioTag HXT.xpickle
) -- GenerateLessonColour.
) where
defaultStyle = Data.Default.def