diff --git a/app/Index.hs b/app/Index.hs new file mode 100644 index 0000000..73a7fca --- /dev/null +++ b/app/Index.hs @@ -0,0 +1,66 @@ +{-# Language DuplicateRecordFields, + NoFieldSelectors, + OverloadedRecordDot, + OverloadedStrings, + DeriveGeneric #-} + +module Index where + + import Data.Yaml + import Control.Applicative hiding (empty) + import GHC.Generics (Generic) + import Data.Map + import Data.Maybe (fromMaybe, fromJust) + + type Index = Map String Entry + + data Entry = Entry { + graphFile :: String, + category :: Maybe String, + defScope :: Maybe String, + defDescription :: Maybe Description, + instDescription :: Maybe Description, + instances :: Value + } deriving (Show, Generic) + + instance FromJSON Entry where + parseJSON (Object o) = Entry <$> + o .: "graph-file" <*> + o .:? "category" <*> + o .:? "definition-scope" <*> + o .:? "definition-description" <*> + o .:? "instance-description" <*> + o .: "instances" + parseJSON _ = error "Unexpected yaml" + + type Title = String + type Content = String + + data Description = Description { + fallbackLang :: Maybe String, + fallback :: (Maybe Title, Maybe Content), + translations :: Map String (Maybe Title, Maybe Content) + } deriving (Show, Generic) + + instance FromJSON Description where + parseJSON (Object o) = Description <$> + o .:? "fallback-lang" <*> + o .: "fallback" <*> + o .: "translations" + + english = "en-eu"; + + getDefDescription :: Entry -> (Maybe Title, Maybe Content) + getDefDescription entry = let description = fromJust entry.defDescription + def = description.fallback + in findWithDefault def english description.translations + getInstDescription :: Entry -> (Maybe Title, Maybe Content) + getInstDescription entry = let description = fromJust entry.instDescription + def = description.fallback + in findWithDefault def english description.translations + + getEntryByFile :: String -> Index -> Entry + getEntryByFile file index = query (elems index) file where + query :: [Entry] -> String -> Entry + query [] _ = error $ "No entries left for " ++ file + query (x:xs) file = if x.graphFile == file then x else query xs file \ No newline at end of file diff --git a/app/Main.hs b/app/Main.hs index c5152e1..65636e8 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -10,11 +10,14 @@ module Main where import qualified Data.ByteString.Char8 as BS import Workflow (Workflow, buildData) import Export - import Data.Maybe (fromJust, isNothing) + import Data.Maybe (fromJust, isNothing, isJust, fromMaybe) import Data.Either (isLeft, fromLeft, fromRight) + import Data.List (dropWhileEnd) import Control.Exception (throw) import Text.Regex.TDFA ((=~)) - + import Index (Index, Entry (Entry), getDefDescription, getInstDescription, getEntryByFile) + import Data.Char (isSpace) + --------------------------------------- @@ -38,8 +41,8 @@ module Main where -- exports the graph data to the JSON file specified in the second argument. generateJSON :: [String] -> IO () generateJSON args = do - print $ head args - print $ last args + -- print $ head args + -- print $ last args content <- BS.readFile (head args) let decoded = decodeEither' content :: Either ParseException Workflow if isLeft decoded then throw (fromLeft undefined decoded) else do @@ -60,33 +63,44 @@ module Main where processDirectory src to = listDirectory src >>= filterWorkflows >>= (\ x -> generateForAll x [] Nothing) where filterWorkflows :: [FilePath] -> IO [FilePath] filterWorkflows entries = return $ filter (=~ ".+\\.yaml") entries - generateForAll :: [FilePath] -> [FilePath] -> Maybe FilePath -> IO () -- sources -> targets -> _index.yaml + generateForAll :: [FilePath] -> [(String, FilePath)] -> Maybe FilePath -> IO () -- sources -> targets -> _index.yaml generateForAll [] _ Nothing = fail "_index.yaml not found" - generateForAll [] targets (Just index) = writeIndex (decodeIndex index) targets "]" - generateForAll (x:xs) targets index = let (rel, abs) = defineTarget x + generateForAll [] targets (Just index) = decodeIndex index >>= \x -> writeIndex x targets "]" + generateForAll (x:xs) targets index = let (yaml, rel, abs) = defineTarget x (newIndex, skip) = case index of Just _ -> (index, False) - Nothing -> if x =~ ".+index\\.yaml" then (Just x, True) else (Nothing, False) + Nothing -> if x =~ ".+index\\.yaml" then (Just $ src ++ "/" ++ x, True) else (Nothing, False) in if skip || x `elem` blackList then generateForAll xs targets newIndex - else generateJSON [src ++ "/" ++ x, abs] >> generateForAll xs (rel:targets) newIndex - defineTarget :: FilePath -> (FilePath, FilePath) -- (rel, abs) + else generateJSON [src ++ "/" ++ x, abs] >> generateForAll xs ((yaml, rel):targets) newIndex + defineTarget :: FilePath -> (String, FilePath, FilePath) -- (src, rel, abs) defineTarget x = let (path, match, _) = x =~ "[a-zA-Z0-9+._-]+\\.yaml" :: (String, String, String) (newFile, _, _) = match =~ "\\." :: (String, String, String) relative = "/definitions/" ++ newFile ++ ".json" absolute = to ++ relative - in (relative, absolute) - writeIndex :: Value -> [FilePath] -> String -> IO () -- content of _index.yaml -> targets -> content for index.json - writeIndex _ [] content = writeFile (to ++ "/index.json") ('[':content) - writeIndex index (x:xs) content = let name = x - url = x - description = "" - newContent = (if null xs then "" else ",\n") ++ "{\n\"name\": \"" ++ name + in (match, relative, absolute) + writeIndex :: Index -> [(String, FilePath)] -> String -> IO () -- content of _index.yaml -> targets -> content for index.json + writeIndex index [] content = print index >> writeFile (to ++ "/index.json") ('[':content) + writeIndex index (x:xs) content = let entry = findEntry (fst x) index + (name1, description1) = getDefDescription entry + (name2, description2) = getInstDescription entry + name = if isJust name1 then name1 else name2 + description = if isJust description1 then description1 else description2 + url = snd x + format = dropWhileEnd isSpace . map (\y -> if y == '\n' then ' ' else y) + newContent = (if null xs then "" else ",\n") ++ "{\n\"name\": \"" ++ format (fromMaybe (snd x) name) ++ "\",\n\"description\": \"" - ++ description ++ "\",\n\"url\": \"" ++ url ++ "\"}" + ++ format (fromMaybe "" description) ++ "\",\n\"url\": \"" ++ url ++ "\"}" in writeIndex index xs (newContent ++ content) - decodeIndex :: FilePath -> Value - decodeIndex _ = Null + decodeIndex :: FilePath -> IO Index + decodeIndex path = do + content <- BS.readFile path + let decoded = decodeEither' content :: Either ParseException Index + if isLeft decoded + then throw (fromLeft undefined decoded) + else return $ fromRight undefined decoded + findEntry :: String -> Index -> Entry + findEntry file index = getEntryByFile file index --------------------------------------- diff --git a/editor.css b/editor.css index 0202be8..c3db1a4 100644 --- a/editor.css +++ b/editor.css @@ -307,11 +307,27 @@ label { color: white; transition: all 100ms ease-in-out 0ms; cursor: pointer; - text-align: justify; hyphens: auto; word-wrap: break-word; + overflow: hidden; + text-overflow: clip; + overflow-wrap: break-word; + text-align: left; } +#filecontent div h3 { + text-align: center; +} + +/* #filecontent div p { + text-align: left; + margin-bottom: 10px; + hyphens: auto; + word-wrap: break-word; + overflow: hidden; + text-overflow: ellipsis; +} */ + #curtain { z-index: 50; position: fixed; diff --git a/editor.html b/editor.html index 4a22534..6f9ec5c 100644 --- a/editor.html +++ b/editor.html @@ -1,5 +1,6 @@ + Editor diff --git a/editor.js b/editor.js index 0a95280..4610dc6 100644 --- a/editor.js +++ b/editor.js @@ -128,7 +128,7 @@ var workflowFiles = []; var workflow = {}; const wfGraph = ForceGraph(); -function defineOnClick(item, url) { +function defineOnClick(item, url, title) { item.onclick = (_ => { fetch(url) .then(response => response.json()) @@ -138,6 +138,7 @@ function defineOnClick(item, url) { workflow[key] = data[key]; prepareWorkflow(); updateGraph(); + document.title = title; }); }); } @@ -148,16 +149,17 @@ fetch('http://localhost:8080/index.json') workflowFiles = data; for (var i = 0; i < workflowFiles.length; i++) { var item = document.createElement('div'); - item.innerHTML = workflowFiles[i].name; + item.innerHTML = '

' + workflowFiles[i].name + '

' + workflowFiles[i].description; var url = 'http://localhost:8080' + workflowFiles[i].url; - defineOnClick(item, url); + defineOnClick(item, url, workflowFiles[i].name + ' | Editor'); fileContent.appendChild(item); } - var url = 'http://localhost:8080' + data[0].url; + var url = 'http://localhost:8080' + workflowFiles[0].url; return fetch(url); }) .then((response) => response.json()) .then((data) => { + document.title = workflowFiles[0].name + ' | Editor'; for (var key in data) workflow[key] = data[key]; wfGraph(document.getElementById('graph')).graphData({nodes: workflow.states, links: workflow.actions}); diff --git a/workflow-visualiser.cabal b/workflow-visualiser.cabal index 18b8aa1..8ef928a 100644 --- a/workflow-visualiser.cabal +++ b/workflow-visualiser.cabal @@ -26,7 +26,8 @@ executable workflow-visualiser -- Modules included in this executable, other than Main. other-modules: Workflow, - Export + Export, + Index -- LANGUAGE extensions used by modules in this package. -- other-extensions: diff --git a/workflows/index.json b/workflows/index.json index b7b8ebe..3e0d140 100644 --- a/workflows/index.json +++ b/workflows/index.json @@ -1,40 +1,40 @@ [{ -"name": "/definitions/theses.json", -"description": "", +"name": "Theses Informatics (IfI)", +"description": "Registration, management, and digital submission of (bachelor's and master's) theses", "url": "/definitions/theses.json"}, { -"name": "/definitions/theses-media.json", -"description": "", +"name": "Theses Media Informatics (IfI)", +"description": "Registration, management, and digital submission of (bachelor's and master's) theses", "url": "/definitions/theses-media.json"}, { -"name": "/definitions/rooms-mi.json", -"description": "", +"name": "Reporting of Room Allocation (MI)", +"description": "

Here, members of staff charged to do so, can report the alloction of offices within their area of competence.

", "url": "/definitions/rooms-mi.json"}, { -"name": "/definitions/recognitions-ifi.json", -"description": "", +"name": "Recognitions (IfI)", +"description": "Apply here for standard recognitions. Please follow the instructions and help texts provided. For complex recognitions that cannot be handled here, please send an email or apply in the programme-coordination-consultation hours.", "url": "/definitions/recognitions-ifi.json"}, { -"name": "/definitions/master-practical-training.json", -"description": "", +"name": "Master individual practical training (IfI)", +"description": "Here you can report grades for an individual practical training as part of the master informatics.", "url": "/definitions/master-practical-training.json"}, { -"name": "/definitions/general-eo-tickets.json", -"description": "", +"name": "General Exam-Office-Tickets", +"description": "Here you can view all the general exam-office-tickets that concern you.", "url": "/definitions/general-eo-tickets.json"}, { -"name": "/definitions/diploma.json", -"description": "", +"name": "Diplomas (IfI)", +"description": "Here you can view the status of your diploma", "url": "/definitions/diploma.json"}, { -"name": "/definitions/cs-minor-degrees.json", -"description": "", +"name": "Computer Science minor degrees (IfI)", +"description": "Here you can request your degree for the following minor degree programmes: - Minor Bachelor Computer Science, 30 ECTS - Minor Bachelor Computer Science, 60 ECTS - Minor Bachelor Mediainformatics, 60 ECTS - Minor Master Computer Science, 30 ECTS", "url": "/definitions/cs-minor-degrees.json"}, { -"name": "/definitions/cip-courses-mi.json", -"description": "", +"name": "Registration for CIP introductory course (MI, S22)", +"description": "

Hier können Sie sich für die ca. einstündige Einführungsveranstaltung anmelden, die Voraussetzung ist um die CIP-Rechner am Mathematischen Institut verwenden zu können.

Eine entsprechende Kennung wird Ihnen im Rahmen der Einführungsveranstaltung zugeteilt.

Berechtigt für Kennungen sind Studierende, die an der Fakultät 16 in einem (Wirtschafts-)Mathematikstudiengang eingeschrieben sind und Lehramtsstudierende mit Unterrichtsfach Mathematik.

Falls Sie bereits eine Kennung zugeteilt bekommen haben und Sie diese nur vergessen haben, brauchen Sie nicht erneut an einer Einführungsveranstaltung teilzunehmen.
Kommen Sie in diesem Fall bitte einfach in die CIP-Betreuung.

", "url": "/definitions/cip-courses-mi.json"}, { -"name": "/definitions/certificates.json", -"description": "", +"name": "Certificates (IfI)", +"description": "Submission and application of certain certificates.", "url": "/definitions/certificates.json"}] \ No newline at end of file