Inferenz Studiengänge verdrahte, hat noch Fehler
This commit is contained in:
parent
8c221ad5e8
commit
d65b5918f0
@ -346,7 +346,6 @@ NoTableContent: Kein Tabelleninhalt
|
|||||||
NoUpcomingSheetDeadlines: Keine anstehenden Übungsblätter
|
NoUpcomingSheetDeadlines: Keine anstehenden Übungsblätter
|
||||||
|
|
||||||
AdminHeading: Administration
|
AdminHeading: Administration
|
||||||
AdminFeaturesHeading: Studiengänge
|
|
||||||
AdminUserHeading: Benutzeradministration
|
AdminUserHeading: Benutzeradministration
|
||||||
AccessRightsFor: Berechtigungen für
|
AccessRightsFor: Berechtigungen für
|
||||||
AdminFor: Administrator
|
AdminFor: Administrator
|
||||||
@ -408,6 +407,8 @@ SheetCorrectorSubmissionsTip: Abgabe erfolgt über ein Uni2work-externes Verfahr
|
|||||||
|
|
||||||
SubmissionNoUploadExpected: Es ist keine Abgabe von Dateien vorgesehen.
|
SubmissionNoUploadExpected: Es ist keine Abgabe von Dateien vorgesehen.
|
||||||
|
|
||||||
|
AdminFeaturesHeading: Studiengänge
|
||||||
|
StudyFeatureInference: Studiengangschlüssel-Inferenz
|
||||||
StudyFeatureAge: Fachsemester
|
StudyFeatureAge: Fachsemester
|
||||||
StudyFeatureDegree: Abschluss
|
StudyFeatureDegree: Abschluss
|
||||||
FieldPrimary: Hauptfach
|
FieldPrimary: Hauptfach
|
||||||
@ -423,6 +424,11 @@ StudyTermsShort: Studiengangkürzel
|
|||||||
StudyTermsChangeSuccess: Zuordnung Abschlüsse aktualisiert
|
StudyTermsChangeSuccess: Zuordnung Abschlüsse aktualisiert
|
||||||
StudyDegreeChangeSuccess: Zuordnung Studiengänge aktualisiert
|
StudyDegreeChangeSuccess: Zuordnung Studiengänge aktualisiert
|
||||||
StudyCandidateIncidence: Anmeldevorgang
|
StudyCandidateIncidence: Anmeldevorgang
|
||||||
|
AmbiguousCandidatesRemoved n@Int: #{show n} #{pluralDE n "uneindeutiger Kandidat" "uneindeutige Kandiaten"} entfernt
|
||||||
|
RedundantCandidatesRemoved n@Int: #{show n} bereits #{pluralDE n "bekannter Kandidat" "bekannte Kandiaten"} entfernt
|
||||||
|
CandidatesInferred n@Int: #{show n} neue #{pluralDE n "Studiengangszuordnung" "Studiengangszuordnungen"} inferiert
|
||||||
|
NoCandidatesInferred: Keine neuen Studienganszuordnungen inferiert
|
||||||
|
StudyTermIsNew: Neu
|
||||||
|
|
||||||
MailTestFormEmail: Email-Addresse
|
MailTestFormEmail: Email-Addresse
|
||||||
MailTestFormLanguages: Spracheinstellungen
|
MailTestFormLanguages: Spracheinstellungen
|
||||||
|
|||||||
@ -49,17 +49,20 @@ StudyDegree -- Studienabschluss
|
|||||||
shorthand Text Maybe -- admin determined shorthand
|
shorthand Text Maybe -- admin determined shorthand
|
||||||
name Text Maybe -- description given by LDAP
|
name Text Maybe -- description given by LDAP
|
||||||
Primary key -- column key is used as actual DB row key
|
Primary key -- column key is used as actual DB row key
|
||||||
|
-- newtype Key StudyDegree = StudyDegreeKey' { unStudyDegreeKey :: Int }
|
||||||
|
deriving Show
|
||||||
StudyTerms -- Studiengang
|
StudyTerms -- Studiengang
|
||||||
key Int -- LMU-internal key
|
key Int -- LMU-internal key
|
||||||
shorthand Text Maybe -- admin determined shorthand
|
shorthand Text Maybe -- admin determined shorthand
|
||||||
name Text Maybe -- description given by LDAP
|
name Text Maybe -- description given by LDAP
|
||||||
Primary key -- column key is used as actual DB row key
|
Primary key -- column key is used as actual DB row key
|
||||||
-- newtype Key StudyTerms = StudyTermsKey { unStudyTermsKey :: Int }
|
-- newtype Key StudyTerms = StudyTermsKey' { unStudyTermsKey :: Int }
|
||||||
|
deriving Show
|
||||||
StudyTermCandidate -- No one at LMU is willing and able to tell us the meaning of the keys for StudyDegrees and StudyTerms.
|
StudyTermCandidate -- No one at LMU is willing and able to tell us the meaning of the keys for StudyDegrees and StudyTerms.
|
||||||
-- Each LDAP login provides an unordered set of keys and an unordered set of plain text description with an unknown 1-1 correspondence.
|
-- Each LDAP login provides an unordered set of keys and an unordered set of plain text description with an unknown 1-1 correspondence.
|
||||||
-- This table helps us to infer which key belongs to which plain text by recording possible combinations at login.
|
-- This table helps us to infer which key belongs to which plain text by recording possible combinations at login.
|
||||||
-- If a login provides n keys and n plan texts, then n^2 rows with the same incidence are created, storing all combinations
|
-- If a login provides n keys and n plan texts, then n^2 rows with the same incidence are created, storing all combinations
|
||||||
incidence UUID -- random id, generated once per login to associate matching pairs
|
incidence TermCandidateIncidence -- random id, generated once per login to associate matching pairs
|
||||||
key Int -- a possible key for the studyTermName
|
key Int -- a possible key for the studyTermName
|
||||||
name Text -- studyTermName as plain text from LDAP
|
name Text -- studyTermName as plain text from LDAP
|
||||||
deriving Show Eq Ord
|
deriving Show Eq Ord
|
||||||
|
|||||||
@ -2097,11 +2097,11 @@ instance YesodAuth UniWorX where
|
|||||||
fs <- either (\err -> throwError . ServerError $ "Could not parse features of study: " <> err) return userStudyFeatures
|
fs <- either (\err -> throwError . ServerError $ "Could not parse features of study: " <> err) return userStudyFeatures
|
||||||
|
|
||||||
let
|
let
|
||||||
studyTermCandidates = Set.fromList $ do
|
studyTermCandidates = do
|
||||||
studyTermCandidateName <- termNames
|
studyTermCandidateName <- termNames
|
||||||
StudyFeatures{ studyFeaturesField = StudyTermsKey' studyTermCandidateKey } <- fs
|
StudyFeatures{ studyFeaturesField = StudyTermsKey' studyTermCandidateKey } <- fs
|
||||||
return StudyTermCandidate{..}
|
return StudyTermCandidate{..}
|
||||||
lift . insertMany_ $ Set.toList studyTermCandidates
|
lift $ insertMany_ studyTermCandidates
|
||||||
|
|
||||||
lift $ E.updateWhere [StudyFeaturesUser ==. userId] [StudyFeaturesValid =. False]
|
lift $ E.updateWhere [StudyFeaturesUser ==. userId] [StudyFeaturesValid =. False]
|
||||||
forM_ fs $ \f@StudyFeatures{..} -> do
|
forM_ fs $ \f@StudyFeatures{..} -> do
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import Utils.Lens
|
|||||||
-- import Data.Function ((&))
|
-- import Data.Function ((&))
|
||||||
-- import Yesod.Form.Bootstrap3
|
-- import Yesod.Form.Bootstrap3
|
||||||
|
|
||||||
-- import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
|
|
||||||
import Database.Persist.Sql (fromSqlKey)
|
import Database.Persist.Sql (fromSqlKey)
|
||||||
@ -56,7 +56,7 @@ instance Button UniWorX ButtonCreate where
|
|||||||
|
|
||||||
btnClasses CreateMath = [BCIsButton, BCInfo]
|
btnClasses CreateMath = [BCIsButton, BCInfo]
|
||||||
btnClasses CreateInf = [BCIsButton, BCPrimary]
|
btnClasses CreateInf = [BCIsButton, BCPrimary]
|
||||||
-- END Button needed here
|
-- END Button needed only here
|
||||||
|
|
||||||
emailTestForm :: AForm (HandlerT UniWorX IO) (Email, MailContext)
|
emailTestForm :: AForm (HandlerT UniWorX IO) (Email, MailContext)
|
||||||
emailTestForm = (,)
|
emailTestForm = (,)
|
||||||
@ -176,17 +176,40 @@ postAdminErrMsgR = do
|
|||||||
|]
|
|]
|
||||||
|
|
||||||
|
|
||||||
|
-- BEGIN - Buttons needed only for StudyTermCandidateManagement
|
||||||
|
data ButtonInferStudyTerms = ButtonInferStudyTerms
|
||||||
|
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
instance Universe ButtonInferStudyTerms
|
||||||
|
instance Finite ButtonInferStudyTerms
|
||||||
|
|
||||||
|
nullaryPathPiece ''ButtonInferStudyTerms camelToPathPiece
|
||||||
|
|
||||||
|
instance Button UniWorX ButtonInferStudyTerms where
|
||||||
|
btnLabel ButtonInferStudyTerms = "Studienfachzuordnung automatisch lernen"
|
||||||
|
btnClasses ButtonInferStudyTerms = [BCIsButton, BCPrimary]
|
||||||
|
-- END Button needed only here
|
||||||
|
|
||||||
getAdminFeaturesR, postAdminFeaturesR :: Handler Html
|
getAdminFeaturesR, postAdminFeaturesR :: Handler Html
|
||||||
getAdminFeaturesR = postAdminFeaturesR
|
getAdminFeaturesR = postAdminFeaturesR
|
||||||
postAdminFeaturesR = do
|
postAdminFeaturesR = do
|
||||||
|
((btnResult, btnWdgt), btnEnctype) <- runFormPost $ identifyForm ("infer-button" :: Text) (buttonForm :: Form ButtonInferStudyTerms)
|
||||||
|
(infConflicts,infAccepted) <- case btnResult of
|
||||||
|
(FormSuccess ButtonInferStudyTerms) -> do
|
||||||
|
(infConflicts,infAmbiguous,infRedundant,infAccepted) <- Candidates.inferHandler
|
||||||
|
unless (null infAmbiguous) $ addMessageI Info $ MsgAmbiguousCandidatesRemoved $ length infAmbiguous
|
||||||
|
unless (null infRedundant) $ addMessageI Info $ MsgRedundantCandidatesRemoved $ length infRedundant
|
||||||
|
if (null infAccepted)
|
||||||
|
then addMessageI Info $ MsgNoCandidatesInferred
|
||||||
|
else addMessageI Success $ MsgCandidatesInferred $ length infAccepted
|
||||||
|
return (infConflicts,infAccepted)
|
||||||
|
_other -> (,[]) <$> runDB Candidates.conflicts
|
||||||
|
unless (null infConflicts) $ addMessage Warning "KONFLIKTE vorhanden" --TODO i18n
|
||||||
|
|
||||||
( (degreeResult,degreeTable)
|
( (degreeResult,degreeTable)
|
||||||
, (studyTermsResult,studytermsTable)
|
, (studyTermsResult,studytermsTable)
|
||||||
, conflicted
|
, ((),candidateTable)) <- runDB $ (,,)
|
||||||
, ((),candidateTable)) <- runDB $ (,,,)
|
|
||||||
<$> mkDegreeTable
|
<$> mkDegreeTable
|
||||||
<*> mkStudytermsTable
|
<*> mkStudytermsTable (Set.fromList $ map (StudyTermsKey' . fst) infAccepted)
|
||||||
<*> Candidates.conflicts
|
|
||||||
<*> mkCandidateTable
|
<*> mkCandidateTable
|
||||||
|
|
||||||
let degreeResult' :: FormResult (Map (Key StudyDegree) (Maybe Text, Maybe Text))
|
let degreeResult' :: FormResult (Map (Key StudyDegree) (Maybe Text, Maybe Text))
|
||||||
@ -211,25 +234,7 @@ postAdminFeaturesR = do
|
|||||||
|
|
||||||
siteLayoutMsg MsgAdminFeaturesHeading $ do
|
siteLayoutMsg MsgAdminFeaturesHeading $ do
|
||||||
setTitleI MsgAdminFeaturesHeading
|
setTitleI MsgAdminFeaturesHeading
|
||||||
[whamlet|
|
$(widgetFile "adminFeatures")
|
||||||
<div .container>
|
|
||||||
<section>
|
|
||||||
^{degreeTable}
|
|
||||||
<div .container>
|
|
||||||
<section>
|
|
||||||
^{studytermsTable}
|
|
||||||
<div .container>
|
|
||||||
<section>
|
|
||||||
$if null conflicted
|
|
||||||
Kein Konflikte beobachtet.
|
|
||||||
$else
|
|
||||||
<h3>Studiengangseingträge mit beobachteten Konflikten:
|
|
||||||
<ul>
|
|
||||||
$forall (Entity _ (StudyTerms ky _ nm)) <- conflicted
|
|
||||||
<li> #{show ky} - #{foldMap id nm}
|
|
||||||
<div .container>
|
|
||||||
^{candidateTable}
|
|
||||||
|]
|
|
||||||
where
|
where
|
||||||
textInputCell lensRes lensDefault = formCell id (return . view (_dbrOutput . _entityKey))
|
textInputCell lensRes lensDefault = formCell id (return . view (_dbrOutput . _entityKey))
|
||||||
(\row _mkUnique -> (\(res,fieldView) -> (set lensRes <$> res, fvInput fieldView))
|
(\row _mkUnique -> (\(res,fieldView) -> (set lensRes <$> res, fvInput fieldView))
|
||||||
@ -246,23 +251,24 @@ postAdminFeaturesR = do
|
|||||||
dbtRowKey = (E.^. StudyDegreeKey)
|
dbtRowKey = (E.^. StudyDegreeKey)
|
||||||
dbtProj = return
|
dbtProj = return
|
||||||
dbtColonnade = formColonnade $ mconcat
|
dbtColonnade = formColonnade $ mconcat
|
||||||
[ sortable (Just "degree-key") (i18nCell MsgDegreeKey) (numCell . view (_dbrOutput . _entityVal . _studyDegreeKey))
|
[ sortable (Just "key") (i18nCell MsgDegreeKey) (numCell . view (_dbrOutput . _entityVal . _studyDegreeKey))
|
||||||
, sortable (Just "degree-name") (i18nCell MsgDegreeName) (textInputCell _1 (_dbrOutput . _entityVal . _studyDegreeName))
|
, sortable (Just "name") (i18nCell MsgDegreeName) (textInputCell _1 (_dbrOutput . _entityVal . _studyDegreeName))
|
||||||
, sortable (Just "degree-short") (i18nCell MsgDegreeShort) (textInputCell _2 (_dbrOutput . _entityVal . _studyDegreeShorthand))
|
, sortable (Just "short") (i18nCell MsgDegreeShort) (textInputCell _2 (_dbrOutput . _entityVal . _studyDegreeShorthand))
|
||||||
|
, dbRow
|
||||||
]
|
]
|
||||||
dbtSorting = Map.fromList
|
dbtSorting = Map.fromList
|
||||||
[ ("degree-key" , SortColumn (E.^. StudyDegreeKey))
|
[ ("key" , SortColumn (E.^. StudyDegreeKey))
|
||||||
, ("degree-name" , SortColumn (E.^. StudyDegreeName))
|
, ("name" , SortColumn (E.^. StudyDegreeName))
|
||||||
, ("degree-short", SortColumn (E.^. StudyDegreeShorthand))
|
, ("short", SortColumn (E.^. StudyDegreeShorthand))
|
||||||
]
|
]
|
||||||
dbtFilter = mempty
|
dbtFilter = mempty
|
||||||
dbtFilterUI = mempty
|
dbtFilterUI = mempty
|
||||||
dbtParams = def { dbParamsFormAddSubmit = True } -- dbParamsFormEvaluate = liftHandlerT . (runFormPost . identifyForm "degree-table" - (identForm FIDdegree))}
|
dbtParams = def { dbParamsFormAddSubmit = True } -- dbParamsFormEvaluate = liftHandlerT . (runFormPost . identifyForm "degree-table" - (identForm FIDdegree))}
|
||||||
psValidator = def & defaultSorting [SortAscBy "degree-name", SortAscBy "degree-short", SortAscBy "degree-key"]
|
psValidator = def & defaultSorting [SortAscBy "name", SortAscBy "short", SortAscBy "key"]
|
||||||
in dbTable psValidator DBTable{..}
|
in dbTable psValidator DBTable{..}
|
||||||
|
|
||||||
mkStudytermsTable :: DB (FormResult (DBFormResult (Key StudyTerms) (Maybe Text, Maybe Text) (DBRow (Entity StudyTerms))), Widget)
|
mkStudytermsTable :: Set (Key StudyTerms) -> DB (FormResult (DBFormResult (Key StudyTerms) (Maybe Text, Maybe Text) (DBRow (Entity StudyTerms))), Widget)
|
||||||
mkStudytermsTable =
|
mkStudytermsTable newKeys =
|
||||||
let dbtIdent = "admin-studyterms" :: Text
|
let dbtIdent = "admin-studyterms" :: Text
|
||||||
dbtStyle = def
|
dbtStyle = def
|
||||||
dbtSQLQuery :: E.SqlExpr (Entity StudyTerms) -> E.SqlQuery ( E.SqlExpr (Entity StudyTerms))
|
dbtSQLQuery :: E.SqlExpr (Entity StudyTerms) -> E.SqlQuery ( E.SqlExpr (Entity StudyTerms))
|
||||||
@ -270,19 +276,22 @@ postAdminFeaturesR = do
|
|||||||
dbtRowKey = (E.^. StudyTermsKey)
|
dbtRowKey = (E.^. StudyTermsKey)
|
||||||
dbtProj = return
|
dbtProj = return
|
||||||
dbtColonnade = formColonnade $ mconcat
|
dbtColonnade = formColonnade $ mconcat
|
||||||
[ sortable (Just "studyterms-key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _entityVal . _studyTermsKey))
|
[ sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _entityVal . _studyTermsKey))
|
||||||
, sortable (Just "studyterms-name") (i18nCell MsgStudyTermsName) (textInputCell _1 (_dbrOutput . _entityVal . _studyTermsName))
|
, sortable (Just "isnew") (i18nCell MsgStudyTermIsNew) (isNewCell . flip Set.member newKeys . view (_dbrOutput . _entityKey))
|
||||||
, sortable (Just "studyterms-short") (i18nCell MsgStudyTermsShort) (textInputCell _2 (_dbrOutput . _entityVal . _studyTermsShorthand))
|
, sortable (Just "name") (i18nCell MsgStudyTermsName) (textInputCell _1 (_dbrOutput . _entityVal . _studyTermsName))
|
||||||
|
, sortable (Just "short") (i18nCell MsgStudyTermsShort) (textInputCell _2 (_dbrOutput . _entityVal . _studyTermsShorthand))
|
||||||
|
, dbRow
|
||||||
]
|
]
|
||||||
dbtSorting = Map.fromList
|
dbtSorting = Map.fromList
|
||||||
[ ("studyterms-key" , SortColumn (E.^. StudyTermsKey))
|
[ ("key" , SortColumn (E.^. StudyTermsKey))
|
||||||
, ("studyterms-name" , SortColumn (E.^. StudyTermsName))
|
, ("isnew" , SortColumn (\studyTerm -> studyTerm E.^. StudyTermsId `E.in_` E.valList (Set.toList newKeys)))
|
||||||
, ("studyterms-short", SortColumn (E.^. StudyTermsShorthand))
|
, ("name" , SortColumn (E.^. StudyTermsName))
|
||||||
|
, ("short" , SortColumn (E.^. StudyTermsShorthand))
|
||||||
]
|
]
|
||||||
dbtFilter = mempty
|
dbtFilter = mempty
|
||||||
dbtFilterUI = mempty
|
dbtFilterUI = mempty
|
||||||
dbtParams = def { dbParamsFormAddSubmit = True } -- , dbParamsFormEvaluate = liftHandlerT . runFormPost }
|
dbtParams = def { dbParamsFormAddSubmit = True } -- , dbParamsFormEvaluate = liftHandlerT . runFormPost }
|
||||||
psValidator = def & defaultSorting [SortAscBy "studyterms-name", SortAscBy "studyterms-short", SortAscBy "studyterms-key"]
|
psValidator = def & defaultSorting [SortAscBy "name", SortAscBy "short", SortAscBy "key"]
|
||||||
in dbTable psValidator DBTable{..}
|
in dbTable psValidator DBTable{..}
|
||||||
|
|
||||||
mkCandidateTable =
|
mkCandidateTable =
|
||||||
@ -293,14 +302,15 @@ postAdminFeaturesR = do
|
|||||||
dbtRowKey = (E.^. StudyTermCandidateId)
|
dbtRowKey = (E.^. StudyTermCandidateId)
|
||||||
dbtProj = return
|
dbtProj = return
|
||||||
dbtColonnade = dbColonnade $ mconcat
|
dbtColonnade = dbColonnade $ mconcat
|
||||||
[ sortable (Just "termcandidate-key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _entityVal . _studyTermCandidateKey))
|
[ dbRow
|
||||||
, sortable (Just "termcandidate-name") (i18nCell MsgStudyTermsName) (textCell . view (_dbrOutput . _entityVal . _studyTermCandidateName))
|
, sortable (Just "key") (i18nCell MsgStudyTermsKey) (numCell . view (_dbrOutput . _entityVal . _studyTermCandidateKey))
|
||||||
, sortable (Just "termcandidate-incidence") (i18nCell MsgStudyCandidateIncidence) (pathPieceCell . view (_dbrOutput . _entityVal . _studyTermCandidateIncidence))
|
, sortable (Just "name") (i18nCell MsgStudyTermsName) (textCell . view (_dbrOutput . _entityVal . _studyTermCandidateName))
|
||||||
|
, sortable (Just "incidence") (i18nCell MsgStudyCandidateIncidence) (pathPieceCell . view (_dbrOutput . _entityVal . _studyTermCandidateIncidence))
|
||||||
]
|
]
|
||||||
dbtSorting = Map.fromList
|
dbtSorting = Map.fromList
|
||||||
[ ("termcandidate-key" , SortColumn (E.^. StudyTermCandidateKey))
|
[ ("key" , SortColumn (E.^. StudyTermCandidateKey))
|
||||||
, ("termcandidate-name" , SortColumn (E.^. StudyTermCandidateName))
|
, ("name" , SortColumn (E.^. StudyTermCandidateName))
|
||||||
, ("termcandidate-incidence", SortColumn (E.^. StudyTermCandidateIncidence))
|
, ("incidence", SortColumn (E.^. StudyTermCandidateIncidence))
|
||||||
]
|
]
|
||||||
dbtFilter = Map.fromList
|
dbtFilter = Map.fromList
|
||||||
[ ("key", FilterColumn $ mkExactFilter (E.^. StudyTermCandidateKey))
|
[ ("key", FilterColumn $ mkExactFilter (E.^. StudyTermCandidateKey))
|
||||||
@ -314,6 +324,6 @@ postAdminFeaturesR = do
|
|||||||
, prismAForm (singletonFilter "incidence") mPrev $ aopt (searchField False) (fslI MsgStudyCandidateIncidence)
|
, prismAForm (singletonFilter "incidence") mPrev $ aopt (searchField False) (fslI MsgStudyCandidateIncidence)
|
||||||
]
|
]
|
||||||
dbtParams = def
|
dbtParams = def
|
||||||
psValidator = def & defaultSorting [SortAscBy "termcandidate-key", SortAscBy "termcandidate-name"]
|
psValidator = def & defaultSorting [SortAscBy "key", SortAscBy "name"]
|
||||||
in dbTable psValidator DBTable{..}
|
in dbTable psValidator DBTable{..}
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,10 @@ sqlCell act = mempty & cellContents .~ lift act
|
|||||||
tickmarkCell :: (IsDBTable m a) => Bool -> DBCell m a
|
tickmarkCell :: (IsDBTable m a) => Bool -> DBCell m a
|
||||||
tickmarkCell = cell . toWidget . hasTickmark
|
tickmarkCell = cell . toWidget . hasTickmark
|
||||||
|
|
||||||
|
-- | Maybe display a exclamation icon
|
||||||
|
isNewCell :: (IsDBTable m a) => Bool -> DBCell m a
|
||||||
|
isNewCell = cell . toWidget . isNew
|
||||||
|
|
||||||
-- | Maybe display comment icon linking a given URL or show nothing at all
|
-- | Maybe display comment icon linking a given URL or show nothing at all
|
||||||
commentCell :: IsDBTable m a => Maybe (Route UniWorX) -> DBCell m a
|
commentCell :: IsDBTable m a => Maybe (Route UniWorX) -> DBCell m a
|
||||||
commentCell Nothing = mempty
|
commentCell Nothing = mempty
|
||||||
|
|||||||
@ -28,13 +28,10 @@ import qualified Database.Esqueleto as E
|
|||||||
-- import Database.Esqueleto.Utils as E
|
-- import Database.Esqueleto.Utils as E
|
||||||
|
|
||||||
|
|
||||||
type STKey = Int -- Key StudyTerms -- for convenience, assmued identical to field StudyTermCandidateKey
|
type STKey = Int -- for convenience, assmued identical to field StudyTermCandidateKey
|
||||||
|
|
||||||
data FailedCandidateInference = FailedCandidateInference [Entity StudyTerms]
|
data FailedCandidateInference = FailedCandidateInference [Entity StudyTerms]
|
||||||
deriving (Typeable)
|
deriving (Typeable, Show)
|
||||||
|
|
||||||
instance Show FailedCandidateInference where
|
|
||||||
show (FailedCandidateInference _) = "Failed Candidate Inference" -- TODO
|
|
||||||
|
|
||||||
instance Exception FailedCandidateInference
|
instance Exception FailedCandidateInference
|
||||||
-- Default Instance
|
-- Default Instance
|
||||||
@ -43,54 +40,30 @@ instance Exception FailedCandidateInference
|
|||||||
shortenStudyTerm :: Text -> Text
|
shortenStudyTerm :: Text -> Text
|
||||||
shortenStudyTerm = concatMap (take 4) . splitCamel
|
shortenStudyTerm = concatMap (take 4) . splitCamel
|
||||||
|
|
||||||
-- | Attempt to identify new StudyTerms based on observations
|
-- | Attempt to identify new StudyTerms based on observations, returning:
|
||||||
inferHandler :: Handler ([UUID],([Entity StudyTerms],[Entity StudyTermCandidate],[(STKey,Text)]))
|
-- * list of ambiguous instances that were discarded outright (identical names for differents keys observed in single incidences)
|
||||||
inferHandler = do
|
-- * list of problems, ie. StudyTerms that contradict observed incidences
|
||||||
(ambiguous, problems) <- runDB $ (,) <$> removeAmbiguous <*> conflicts
|
-- * list of redundants, i.e. redundant observed incidences
|
||||||
if (null problems)
|
-- * list of accepted, i.e. newly accepted key/name pairs
|
||||||
then do
|
inferHandler :: Handler ([Entity StudyTerms],[TermCandidateIncidence],[Entity StudyTermCandidate],[(STKey,Text)])
|
||||||
infRes <- inferAcc ([],[])
|
inferHandler = runDB $ inferAcc ([],[],[])
|
||||||
return (ambiguous, infRes)
|
|
||||||
else
|
|
||||||
return (ambiguous,(problems,[],[]))
|
|
||||||
|
|
||||||
where
|
where
|
||||||
inferAcc (accRedundants, accAccepted) =
|
inferAcc (accAmbiguous, accRedundants, accAccepted) =
|
||||||
handle (\(FailedCandidateInference fails) -> return (fails,accRedundants,accAccepted)) $ do
|
handle (\(FailedCandidateInference fails) -> (fails,accAmbiguous,accRedundants,accAccepted) <$ E.transactionUndo) $ do
|
||||||
(infReds,infAccs) <- runDB inferStep
|
(infAmbis, infReds,infAccs) <- inferStep
|
||||||
if null infAccs
|
if null infAccs
|
||||||
then return ([], infReds ++ accRedundants, accAccepted)
|
then return ([], accAmbiguous, infReds ++ accRedundants, accAccepted)
|
||||||
else inferAcc (infReds ++ accRedundants, infAccs ++ accAccepted)
|
else do
|
||||||
|
E.transactionSave -- commit transaction if there are no problems
|
||||||
|
inferAcc (infAmbis ++ accAmbiguous, infReds ++ accRedundants, infAccs ++ accAccepted)
|
||||||
|
|
||||||
inferStep = do
|
inferStep = do
|
||||||
|
ambiguous <- removeAmbiguous
|
||||||
redundants <- removeRedundant
|
redundants <- removeRedundant
|
||||||
accepted <- acceptSingletons
|
accepted <- acceptSingletons
|
||||||
problems <- conflicts
|
problems <- conflicts
|
||||||
when (not $ null problems) $ throw $ FailedCandidateInference problems
|
when (not $ null problems) $ throwM $ FailedCandidateInference problems
|
||||||
return (redundants, accepted)
|
return (ambiguous, redundants, accepted)
|
||||||
|
|
||||||
|
|
||||||
-- | Attempt to identify new StudyTerms based on observations
|
|
||||||
-- infer :: MonadHandler m => m ([Entity StudyTerms],[Entity StudyTerms])
|
|
||||||
infer :: DB ([Entity StudyTerms],[(STKey, Text)])
|
|
||||||
infer = do
|
|
||||||
void removeAmbiguous -- TODO: show result
|
|
||||||
inferAcc []
|
|
||||||
where
|
|
||||||
inferAcc prevSet = do
|
|
||||||
problems <- conflicts
|
|
||||||
if null problems
|
|
||||||
then do
|
|
||||||
void removeRedundant -- TODO: show result
|
|
||||||
newSet <- acceptSingletons
|
|
||||||
if null newSet
|
|
||||||
then -- inference complete
|
|
||||||
return ([],prevSet)
|
|
||||||
else
|
|
||||||
inferAcc (newSet ++ prevSet)
|
|
||||||
else --abort
|
|
||||||
return (problems,prevSet)
|
|
||||||
|
|
||||||
|
|
||||||
{-
|
{-
|
||||||
Candidate 1 11 "A"
|
Candidate 1 11 "A"
|
||||||
@ -113,7 +86,7 @@ as a fix we simply eliminate all observations that have the same name twice, see
|
|||||||
-- | remove candidates with ambiguous observations,
|
-- | remove candidates with ambiguous observations,
|
||||||
-- ie. candidates that have duplicated term names with differing keys
|
-- ie. candidates that have duplicated term names with differing keys
|
||||||
-- which may happen in rare cases
|
-- which may happen in rare cases
|
||||||
removeAmbiguous :: DB [UUID]
|
removeAmbiguous :: DB [TermCandidateIncidence]
|
||||||
removeAmbiguous = do
|
removeAmbiguous = do
|
||||||
ambiList <- E.select $ E.from $ \(candA `E.InnerJoin` candB) -> do
|
ambiList <- E.select $ E.from $ \(candA `E.InnerJoin` candB) -> do
|
||||||
-- Either an innerJoin with itself or an exists-sub-select
|
-- Either an innerJoin with itself or an exists-sub-select
|
||||||
@ -165,7 +138,7 @@ acceptSingletons = do
|
|||||||
groupedCandidates = foldl' groupFun mempty incidences
|
groupedCandidates = foldl' groupFun mempty incidences
|
||||||
|
|
||||||
-- given a key, map each incidence to set of possible names for this key
|
-- given a key, map each incidence to set of possible names for this key
|
||||||
groupFun :: Map STKey (Map UUID (Set Text)) -> StudyTermCandidate -> Map STKey (Map UUID (Set Text))
|
groupFun :: Map STKey (Map TermCandidateIncidence (Set Text)) -> StudyTermCandidate -> Map STKey (Map TermCandidateIncidence (Set Text))
|
||||||
groupFun m StudyTermCandidate{..} =
|
groupFun m StudyTermCandidate{..} =
|
||||||
insertWith (Map.unionWith Set.union)
|
insertWith (Map.unionWith Set.union)
|
||||||
studyTermCandidateKey
|
studyTermCandidateKey
|
||||||
@ -192,37 +165,11 @@ acceptSingletons = do
|
|||||||
-- insertKey (StudyTermsKey key) $ StudyTerms key (Just $ shortenStudyTerm name) (Just name) -- name clash!
|
-- insertKey (StudyTermsKey key) $ StudyTerms key (Just $ shortenStudyTerm name) (Just name) -- name clash!
|
||||||
void . insert $ StudyTerms key (Just $ shortenStudyTerm name) (Just name)
|
void . insert $ StudyTerms key (Just $ shortenStudyTerm name) (Just name)
|
||||||
|
|
||||||
|
|
||||||
-- register newly fixed candidates
|
-- register newly fixed candidates
|
||||||
forM_ fixedKeys registerFixed
|
forM_ fixedKeys registerFixed
|
||||||
return fixedKeys
|
return fixedKeys
|
||||||
|
|
||||||
|
|
||||||
-- SOME EARLIER ATTEMPTS FOLLOW:
|
|
||||||
--
|
|
||||||
-- unknownKeys <- E.select $ E.distinct $ E.from $ \candidate -> do
|
|
||||||
-- E.where_ $ E.notExists $ E.from $ \sterm ->
|
|
||||||
-- E.where_ $ candidate E.^. StudyTermCandidateKey E.==. sterm E.^. StudyTermKey
|
|
||||||
-- return $ candidate E.^. StudyTermCandidateKey
|
|
||||||
-- forM unknownKeys $ \(E.Value key) -> do
|
|
||||||
-- incidences <- E.select $ E.from $ \candidate -> do
|
|
||||||
-- E.where_ $
|
|
||||||
--
|
|
||||||
-- -- DON'T KNOW HOW TO DO IN SQL :( BUT WE NEED THE ENTIRE TABLE ANYHOW
|
|
||||||
-- candidates <- entityVal <$> selectList [] [] -- load entire candidate table
|
|
||||||
-- -- create map from UUID to set of candidates for efficiency
|
|
||||||
-- let collectCandidates m stc@StudyTermCandidate{studyTermCandidateIncidence=inci}
|
|
||||||
-- = insertWith Set.union inci stc
|
|
||||||
-- incidences = foldl collectCandidates Map.empty candidates
|
|
||||||
--
|
|
||||||
-- collectKeys m
|
|
||||||
-- keySets = foldl collectKeys Map.empty candidates
|
|
||||||
--
|
|
||||||
-- -- StudyTermCandidateKey -> Set StudyTermCandidateName
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | all existing StudyTerms that are contradiced by current observations
|
-- | all existing StudyTerms that are contradiced by current observations
|
||||||
conflicts :: DB [Entity StudyTerms]
|
conflicts :: DB [Entity StudyTerms]
|
||||||
conflicts = E.select $ E.from $ \studyTerms -> do
|
conflicts = E.select $ E.from $ \studyTerms -> do
|
||||||
|
|||||||
@ -785,3 +785,4 @@ type UserEmail = CI Email
|
|||||||
|
|
||||||
type PWHashAlgorithm = ByteString -> PWStore.Salt -> Int -> ByteString
|
type PWHashAlgorithm = ByteString -> PWStore.Salt -> Int -> ByteString
|
||||||
type InstanceId = UUID
|
type InstanceId = UUID
|
||||||
|
type TermCandidateIncidence = UUID
|
||||||
|
|||||||
@ -161,6 +161,11 @@ hasTickmark :: Bool -> Markup
|
|||||||
hasTickmark True = [shamlet|<i .fas .fa-check>|]
|
hasTickmark True = [shamlet|<i .fas .fa-check>|]
|
||||||
hasTickmark False = mempty
|
hasTickmark False = mempty
|
||||||
|
|
||||||
|
isNew :: Bool -> Markup
|
||||||
|
isNew True = [shamlet|<i .fas .fa-exclamation>|]
|
||||||
|
isNew False = mempty
|
||||||
|
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
-- Text and String --
|
-- Text and String --
|
||||||
---------------------
|
---------------------
|
||||||
|
|||||||
19
templates/adminFeatures.hamlet
Normal file
19
templates/adminFeatures.hamlet
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<section>
|
||||||
|
^{degreeTable}
|
||||||
|
<section>
|
||||||
|
^{studytermsTable}
|
||||||
|
<section>
|
||||||
|
<h2>_{MsgStudyFeatureInference}
|
||||||
|
<p>
|
||||||
|
$if null infConflicts
|
||||||
|
Kein Konflikte beobachtet.
|
||||||
|
$else
|
||||||
|
<h3>Studiengangseingträge mit beobachteten Konflikten:
|
||||||
|
<ul>
|
||||||
|
$forall (Entity _ (StudyTerms ky _ nm)) <- infConflicts
|
||||||
|
<li> #{show ky} - #{foldMap id nm}
|
||||||
|
<form .form-inline method=post action=@{AdminFeaturesR} enctype=#{btnEnctype}>
|
||||||
|
^{btnWdgt}
|
||||||
|
|
||||||
|
<div .container>
|
||||||
|
^{candidateTable}
|
||||||
Reference in New Issue
Block a user