1 {-# LANGUAGE DuplicateRecordFields #-}
2 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE ScopedTypeVariables #-}
5 {-# LANGUAGE UndecidableInstances #-}
6 {-# OPTIONS_GHC -fno-warn-orphans #-}
7 module Hdoc.DTC.Write.HTML5.Judgment where
9 import Control.Monad (Monad(..), join, forM, forM_)
10 import Data.Default.Class (Default(..))
11 import Data.Either (Either(..))
12 import Data.Eq (Eq(..))
13 import Data.Foldable (Foldable(..))
14 import Data.Function (($), (.))
15 import Data.Functor ((<$>))
17 import Data.Locale hiding (Index)
18 import Data.Maybe (Maybe(..), maybe, maybeToList, listToMaybe, fromMaybe, isJust)
19 import Data.Monoid (Monoid(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.String (String)
22 import Data.Tuple (snd)
23 import Prelude ((*), Fractional(..), Double, toRational, RealFrac(..))
24 import Text.Blaze ((!))
25 import Text.Show (Show(..))
26 import qualified Control.Monad.Trans.State as S
27 import qualified Data.Char as Char
28 import qualified Data.HashMap.Strict as HM
29 import qualified Data.HashSet as HS
30 import qualified Data.List as List
31 import qualified Data.Map.Strict as Map
32 import qualified Data.Set as Set
33 import qualified Data.Text.Lazy as TL
34 import qualified Data.Tree as Tree
35 import qualified Hjugement as MJ
36 import qualified Prelude (error)
37 import qualified Text.Blaze.Html5 as H
38 import qualified Text.Blaze.Html5.Attributes as HA
40 import Hdoc.DTC.Document as DTC
41 import Hdoc.DTC.Write.HTML5.Base
42 import Hdoc.DTC.Write.HTML5.Ident
43 import Hdoc.DTC.Write.XML ()
44 import Control.Monad.Utils
45 import Text.Blaze.Utils
46 import qualified Hdoc.XML as XML
47 import qualified Hdoc.DTC.Collect as Collect
48 import qualified Hdoc.DTC.Write.Plain as Plain
52 showJudgments :: HM.HashMap (Ident,Ident,Maybe Title) [Tree.Tree [Choice]] -> String
56 -- Tree.Node (Left ("","",Nothing)) $
57 (<$> HM.toList js) $ \((j,g,q),ts) ->
59 (Left (unIdent j,unIdent g,Plain.text def <$> q))
63 instance Html5ify Title => Html5ify Judgment where
64 html5ify Judgment{..} = do
65 liftComposeState $ S.modify' $ \s -> s
66 { state_styles = HS.insert (Left "dtc-judgment.css") $ state_styles s }
67 H.div ! HA.id (attrify $ identify $ XML.pos_ancestors judgment_posXML) $$ do
68 let commentJGC = HM.fromList
69 [ (choice_, HM.fromListWith (<>)
70 [ (opinion_grade, HM.singleton opinion_judge opinion_comment)
71 | Opinion{..} <- choice_opinions ])
72 | choice_@Choice{..} <- judgment_choices ]
73 case judgment_question of
75 Just title -> H.div ! HA.class_ "judgment-question" $$ html5ify title
76 H.dl ! HA.class_ "judgment-choices" $$ do
77 case judgment_opinionsByChoice of
79 forM_ judgment_choices $ \Choice{..} -> do
80 H.dt ! HA.class_ "choice-title"
81 ! HA.id (attrify $ identify $ XML.pos_ancestors choice_posXML) $$ do
83 Just distByJudgeByChoice -> do
84 let meritByChoice@(MJ.MeritByChoice meritC) = MJ.meritByChoice distByJudgeByChoice
85 let ranking = MJ.majorityRanking meritByChoice
86 forM_ ranking $ \(choice_@DTC.Choice{..}, majorityValue) -> do
87 H.dt ! HA.class_ "choice-title"
88 ! HA.id (attrify $ identify $ XML.pos_ancestors choice_posXML) $$ do
90 H.dd ! HA.class_ "choice-merit" $$ do
91 let distByJudge = distByJudgeByChoice HM.!choice_
92 let numJudges = HM.size distByJudge
93 html5MeritHistogram majorityValue numJudges
94 let grades = Map.keys $ MJ.unMerit $ meritC HM.!choice_
95 let commentJG = HM.lookup choice_ commentJGC
96 html5MeritComments distByJudge grades commentJG
97 instance Html5ify Judges where
99 html5CommonAttrs judges_attrs
100 { classes = "judges":classes judges_attrs
101 , DTC.id = Just $ Ident $ Plain.text def $ XML.pos_ancestors judges_posXML
106 html5MeritComments ::
108 MJ.Opinions Name (MJ.Ranked Grade) ->
110 Maybe (HM.HashMap Name (HM.HashMap Name (Maybe Title))) ->
112 html5MeritComments distJ grades commentJG = do
113 Loqualization l10n <- liftComposeState $ S.gets state_l10n
114 H.ul ! HA.class_ "merit-comments" $$ do
116 grade | DTC.Grade{..} <- MJ.unRank grade -> do
117 let commentJ = commentJG >>= HM.lookup grade_name
118 let judgesWithComment =
119 -- FIXME: sort accents better: « e é f » not « e f é »
120 List.sortOn (TL.map Char.toLower . unName . (\(j,_,_) -> j))
121 [ (judge, importance, commentJ >>= HM.lookupDefault Nothing judge)
122 | (judge, dist) <- HM.toList distJ
123 , importance <- maybeToList $ Map.lookup grade dist ]
124 forM_ judgesWithComment $ \(judge, importance, comment) ->
125 H.li ! HA.class_ ("merit-comment" <> if isJust comment then " judge-comment" else "") $$ do
127 ! HA.class_ ("judge" <> if judge`HM.member`fromMaybe HM.empty commentJ then "" else " inactive")
128 ! HA.style ("color:"<>attrify grade_color<>";") $$ do
129 unless (importance == 1) $ do
130 H.span ! HA.class_ "section-importance" $$ do
132 (round::Double -> Int) $
133 fromRational $ importance * 100
134 html5ify $ show percent
140 Plain.l10n_Colon l10n :: HTML5
143 html5MeritHistogram ::
145 MJ.MajorityValue (MJ.Ranked Grade) -> Int -> HTML5
146 html5MeritHistogram (MJ.MajorityValue majVal) numJudges = do
147 H.div ! HA.class_ "merit-histogram" $$ do
149 (grade, count) | DTC.Grade{..} <- MJ.unRank grade -> do
150 let percent :: Double =
151 fromRational $ (toRational $ (ceiling::Double -> Int) $ fromRational $
152 (count / toRational numJudges) * 100 * 1000) / 1000
153 let bcolor = "background-color:"<>attrify grade_color<>";"
154 let width = "width:"<>attrify percent<>"%;"
155 let display = if percent == 0 then "display:none;" else ""
157 ! HA.class_ "merit-grade"
158 ! HA.alt (attrify grade_name) -- FIXME: do not work
159 ! HA.style (bcolor<>display<>width) $$ do
161 ! HA.class_ "grade-name" $$ do
163 Nothing -> html5ify grade_name
166 html5Judgments :: HTML5
168 Collect.All{..} <- liftComposeState $ S.gets state_collect
169 opinionsByChoiceByNodeBySectionByJudgment <-
170 forM (HM.toList all_judgments) $ \(judgment@Judgment{..}, choicesBySection) -> do
171 -- WARNING: only the fields of 'Judgment' used in its 'Hashable' instance
172 -- can safely be used here: 'judgment_judgesId' and 'judgment_gradesId' are ok
174 maybe (Prelude.error $ show judgment_grades) MJ.grades $ -- unknown grades
175 HM.lookup judgment_gradesId all_grades
177 fromMaybe (Prelude.error $ show judgment_judges) $ -- unknown judges
178 HM.lookup judgment_judgesId all_judges
179 let defaultGradeByJudge =
182 [ g | g <- Set.toList judgmentGrades
183 , grade_isDefault $ MJ.unRank g
185 (<$> judges_byName) $ \js ->
186 let Judge{..} = List.head js in
187 let judgeDefaultGrade = do
188 grade <- join $ listToMaybe <$> HM.lookup judgment_gradesId judge_defaultGrades
190 [ g | g <- Set.toList judgmentGrades
191 , grade_name (MJ.unRank g) == grade
193 defaultGrade`fromMaybe`judgeDefaultGrade
194 opinionsByChoiceByNodeBySection <-
195 forM choicesBySection $ \choicesTree -> do
196 judgmentTree <- forM choicesTree $ \(section_importance, choices) -> do
197 judgmentOpinions <- forM choices $ \choice_@DTC.Choice{..} -> do
198 gradeByJudge <- forM choice_opinions $ \Opinion{..} -> do
200 [ g | g <- Set.toList judgmentGrades
201 , grade_name (MJ.unRank g) == opinion_grade
203 Just grd -> return (opinion_judge, MJ.Section opinion_importance (Just grd))
204 Nothing -> Prelude.error $ show opinion_grade -- unknown grade
205 return (choice_, HM.fromList gradeByJudge)
206 return $ MJ.SectionNode section_importance $ HM.fromList judgmentOpinions
207 let judgmentChoices = HS.fromList $ snd $ Tree.rootLabel choicesTree
208 -- NOTE: choices are determined by those at the root Tree.Node.
209 -- NOTE: core Majority Judgment calculus handled here by MJ
210 case MJ.opinionsBySection judgmentChoices defaultGradeByJudge judgmentTree of
211 Right opinionsByChoiceByNode -> return opinionsByChoiceByNode
212 Left err -> Prelude.error $ show err -- unknown choice, unknown judge, invalid shares
213 -- NOTE: 'toList' returns a self-then-descending-then-following traversal of a 'Tree',
214 -- this will match perfectly withw the 'html5ify' traversal:
215 -- 'BodySection' by 'BodySection'.
216 return (judgment, join $ toList <$> opinionsByChoiceByNodeBySection)
217 liftComposeState $ S.modify' $ \st ->
218 st{state_opinions = HM.fromList opinionsByChoiceByNodeBySectionByJudgment}