retrieve & show information from index.yaml

This commit is contained in:
David Mosbach 2023-05-30 16:22:29 +02:00
parent 51f97f8702
commit 033f5e1b8d
7 changed files with 146 additions and 46 deletions

66
app/Index.hs Normal file
View File

@ -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

View File

@ -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
---------------------------------------

View File

@ -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;

View File

@ -1,5 +1,6 @@
<head>
<meta charset="utf-8">
<title>Editor</title>
<!-- <script src="//unpkg.com/force-graph"></script> -->
<script src="https://unpkg.com/force-graph@1.43.0/dist/force-graph.min.js"></script>
<!-- <script src="./force-graph-master/src/force-graph.js"></script> -->

View File

@ -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 = '<h3>' + workflowFiles[i].name + '</h3>' + 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});

View File

@ -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:

View File

@ -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": "<p> Here, members of staff charged to do so, can report the alloction of offices within their area of competence. </p>",
"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": "<p> 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. </p> <p> Eine entsprechende Kennung wird Ihnen im Rahmen der Einführungsveranstaltung zugeteilt. </p> <p> Berechtigt für Kennungen sind Studierende, die an der Fakultät 16 in einem (Wirtschafts-)Mathematikstudiengang eingeschrieben sind und Lehramtsstudierende mit Unterrichtsfach Mathematik. </p> <p> Falls Sie bereits eine Kennung zugeteilt bekommen haben und Sie diese nur vergessen haben, brauchen Sie nicht erneut an einer Einführungsveranstaltung teilzunehmen.<br /> Kommen Sie in diesem Fall bitte einfach in die CIP-Betreuung. </p>",
"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"}]