Commit f912ea67 authored by Henrik Tramberend's avatar Henrik Tramberend
Browse files

Working on the exam generator and some CSS fixes

parent 57924457
...@@ -42,9 +42,6 @@ main = do ...@@ -42,9 +42,6 @@ main = do
let metaA = globA "**/*-meta.yaml" let metaA = globA "**/*-meta.yaml"
-- Read meta data.
-- metaData <- readMetaDataIO meta
-- Calculate targets -- Calculate targets
let decksA = deckSourcesA >>= calcTargets ".md" ".html" let decksA = deckSourcesA >>= calcTargets ".md" ".html"
let decksPdfA = deckSourcesA >>= calcTargets ".md" ".pdf" let decksPdfA = deckSourcesA >>= calcTargets ".md" ".pdf"
......
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad () import Control.Monad ()
import Control.Exception import Control.Exception
import Data.Maybe () import Data.Maybe ()
...@@ -27,13 +29,14 @@ import Test ...@@ -27,13 +29,14 @@ import Test
import Embed import Embed
main :: IO () main :: IO ()
main = main = do
do
-- Calculate some directories -- Calculate some directories
projectDir <- calcProjectDirectory projectDir <- calcProjectDirectory
let privateDir = projectDir </> "private" let privateDir = projectDir </> "private"
-- Find sources -- Find questions
testFiles <- glob "**/*-quest.yaml" questionFiles <- glob "**/*-quest.yaml"
-- Find exams
examFiles <- glob "**/*-exam.yaml"
-- Meta data -- Meta data
metaFiles <- glob "**/*-meta.yaml" metaFiles <- glob "**/*-meta.yaml"
-- Calculate targets -- Calculate targets
...@@ -41,40 +44,53 @@ main = ...@@ -41,40 +44,53 @@ main =
-- Prepare Mustache templates -- Prepare Mustache templates
let templates = compileTesterTemplates let templates = compileTesterTemplates
--- ---
shakeArgs shakeOptions $ shakeArgs
shakeOptions $
do want ["catalog"] do want ["catalog"]
-- --
catalog %> catalog %>
\out -> \out ->
do need testFiles do need questionFiles
allQuestions <- readTests testFiles allQuestions <- readTests questionFiles
renderCatalog projectDir templates allQuestions out renderCatalog projectDir templates allQuestions out
-- --
phony "catalog" $ phony
"catalog" $
do need [catalog] do need [catalog]
-- --
phony "new-mc" $ phony
"new-exam" $
do let string = Y.encodePretty Y.defConfig examStationary
liftIO $ B.writeFile "new-exam.yaml" string
--
phony
"new-mc" $
do let string = Y.encodePretty Y.defConfig multipleChoiceStationary do let string = Y.encodePretty Y.defConfig multipleChoiceStationary
liftIO $ B.writeFile "new-mc-quest.yaml" string liftIO $ B.writeFile "new-mc-quest.yaml" string
-- --
phony "new-ft" $ phony
"new-ft" $
do let string = Y.encodePretty Y.defConfig fillTextStationary do let string = Y.encodePretty Y.defConfig fillTextStationary
liftIO $ B.writeFile "new-ft-quest.yaml" string liftIO $ B.writeFile "new-ft-quest.yaml" string
-- --
phony "new-f" $ phony
"new-f" $
do let string = Y.encodePretty Y.defConfig freeStationary do let string = Y.encodePretty Y.defConfig freeStationary
liftIO $ B.writeFile "new-f-quest.yaml" string liftIO $ B.writeFile "new-f-quest.yaml" string
-- --
phony "clean" $ phony
"clean" $
do removeFilesAfter "." ["private"] do removeFilesAfter "." ["private"]
-- Reads all the questions and returns them along with the base directory of -- Reads all the questions and returns them along with the base directory of
-- each. -- each.
readTests :: [FilePath] -> Action [(Question, FilePath)] readTests
:: [FilePath] -> Action [(Question, FilePath)]
readTests files = mapM readTest files readTests files = mapM readTest files
where readTest :: FilePath -> Action (Question, FilePath) where
readTest file = readTest :: FilePath -> Action (Question, FilePath)
do absolutePath <- liftIO $ makeAbsolute file readTest file = do
absolutePath <- liftIO $ makeAbsolute file
string <- liftIO $ B.readFile absolutePath string <- liftIO $ B.readFile absolutePath
let question = let question =
case Y.decodeEither' string of case Y.decodeEither' string of
...@@ -82,60 +98,84 @@ readTests files = mapM readTest files ...@@ -82,60 +98,84 @@ readTests files = mapM readTest files
Left exception -> Left exception ->
throw $ throw $
YamlException $ YamlException $
"Error parsing YAML file: " ++ "Error parsing YAML file: " ++ file ++ ", " ++ (show exception)
file ++ ", " ++ (show exception)
return (question, takeDirectory absolutePath) return (question, takeDirectory absolutePath)
renderCatalog :: FilePath -> Templates -> [(Question, FilePath)] -> FilePath -> Action() -- Renders a catalog of all questions (TODO sorted by LectureId and TopicId).
renderCatalog projectDir templates questions out = renderCatalog
do let markdown = map (\(q,b) -> (renderMarkdown q,b)) questions :: FilePath -> Templates -> [(Question, FilePath)] -> FilePath -> Action ()
renderCatalog projectDir templates questions out = do
let markdown =
map
(\(q,b) ->
(renderMarkdown q, b))
questions
let pandoc = map parseMarkdown markdown let pandoc = map parseMarkdown markdown
need $ concatMap extractLocalImagePathes pandoc need $ concatMap extractLocalImagePathes pandoc
let catalog = let catalog =
Pandoc nullMeta $ concatMap (\(Pandoc _ blocks) -> blocks) pandoc Pandoc nullMeta $
concatMap
(\(Pandoc _ blocks) ->
blocks)
pandoc
let options = let options =
def {writerStandalone = True def
,writerTemplate = B.unpack testLatexTemplate { writerStandalone = True
,writerHighlight = True , writerTemplate = B.unpack testLatexTemplate
,writerHighlightStyle = pygments , writerHighlight = True
,writerCiteMethod = Citeproc} , writerHighlightStyle = pygments
, writerCiteMethod = Citeproc
}
putNormal $ "# pandoc (for " ++ out ++ ")" putNormal $ "# pandoc (for " ++ out ++ ")"
pandocMakePdf options catalog out pandocMakePdf options catalog out
where parseMarkdown (markdown,base) = where
parseMarkdown (markdown,base) =
case readMarkdown def markdown of case readMarkdown def markdown of
Left err -> throw $ PandocException (show err) Left err -> throw $ PandocException (show err)
Right pandoc -> walk (adjustImageUrls base) pandoc Right pandoc -> walk (adjustImageUrls base) pandoc
adjustImageUrls base (Image attr inlines (url,title)) = adjustImageUrls base (Image attr inlines (url,title)) =
(Image attr inlines (adjustLocalUrl projectDir base url,title)) (Image attr inlines (adjustLocalUrl projectDir base url, title))
adjustImageUrls _ inline = inline adjustImageUrls _ inline = inline
renderMarkdown question = renderMarkdown question =
T.unpack $ T.unpack $ M.substitute (selectTemplate templates question) (MT.mFromJSON question)
M.substitute (selectTemplate templates question)
(MT.mFromJSON question)
-- TODO Make this work -- TODO Make this work
newOrder :: Int -> IO [Int] newOrder
newOrder n = :: Int -> IO [Int]
do gen <- getStdGen newOrder n = do
return $ shuffle' [0..(n-1)] n gen gen <- getStdGen
return $ shuffle' [0 .. (n - 1)] n gen
-- TODO Make this work -- TODO Make this work
shuffleAnswers :: Question -> Action Question shuffleAnswers
:: Question -> Action Question
shuffleAnswers q = shuffleAnswers q =
case qstAnswer q of case qstAnswer q of
MultipleChoice choices correct -> MultipleChoice choices correct -> do
do let n = length choices let n = length choices
order <- liftIO $ newOrder n order <- liftIO $ newOrder n
return q {qstAnswer = return
MultipleChoice (shuffle choices order) q
(shuffle correct order)} { qstAnswer = MultipleChoice (shuffle choices order) (shuffle correct order)
}
otherwise -> return q otherwise -> return q
examStationary :: Exam
examStationary =
Exam
{ examStudentInfoFile = "PATH/TO/STUDENT/INFO"
, examDateTime = "DATE_TIME"
, examDurationInMinutes = "DURATION_IN_MINUTES"
, examTrack = 1
, examLectureIds = ["LECTURE_ID"]
, examExcludedTopicIds = ["EXCLUDED_TOPIC_ID"]
}
multipleChoiceStationary :: Question multipleChoiceStationary :: Question
multipleChoiceStationary = multipleChoiceStationary =
Question Question
{ qstId = "ID" { qstTopicId = "TOPIC_ID"
, qstLecture = 0 , qstLectureId = "LECTURE_ID"
, qstTitle = "MULTIPLE CHOICE" , qstTitle = "MULTIPLE CHOICE"
, qstPoints = 5 , qstPoints = 5
, qstQuestion = "THE QUESTION?" , qstQuestion = "THE QUESTION?"
...@@ -150,8 +190,8 @@ multipleChoiceStationary = ...@@ -150,8 +190,8 @@ multipleChoiceStationary =
fillTextStationary :: Question fillTextStationary :: Question
fillTextStationary = fillTextStationary =
Question Question
{ qstId = "ID" { qstTopicId = "TOPIC_ID"
, qstLecture = 0 , qstLectureId = "LECTURE_ID"
, qstTitle = "FILL TEXT" , qstTitle = "FILL TEXT"
, qstPoints = 5 , qstPoints = 5
, qstQuestion = "THE QUESTION?" , qstQuestion = "THE QUESTION?"
...@@ -166,8 +206,8 @@ fillTextStationary = ...@@ -166,8 +206,8 @@ fillTextStationary =
freeStationary :: Question freeStationary :: Question
freeStationary = freeStationary =
Question Question
{ qstId = "ID" { qstTopicId = "TOPIC_ID"
, qstLecture = 0 , qstLectureId = "LECTURE_ID"
, qstTitle = "FREE" , qstTitle = "FREE"
, qstPoints = 5 , qstPoints = 5
, qstQuestion = "THE QUESTION?" , qstQuestion = "THE QUESTION?"
......
Track: 1
LectureIds:
- LECTURE_ID
ExcludeTopicIds:
- EXCLUDED_TOPIC_ID
DurationInMinutes: DURATION_IN_MINUTES
StudentInfoFile: PATH/TO/STUDENT/INFO
DateTime: DATE_TIME
...@@ -20,8 +20,7 @@ transition: linear ...@@ -20,8 +20,7 @@ transition: linear
## Pandoc-Markdown ## Pandoc-Markdown
- Slides are basically [Pandoc-Markdown](http://pandoc.org) formatted - Slides are basically [Pandoc-Markdown](http://pandoc.org) formatted text
text
- Pandoc provides a Markdown variant with many extensions - Pandoc provides a Markdown variant with many extensions
## Some Pandoc extensions ## Some Pandoc extensions
...@@ -55,7 +54,7 @@ transition: linear ...@@ -55,7 +54,7 @@ transition: linear
The following text ist included from file `/resource/realtive.md`: The following text ist included from file `/resource/realtive.md`:
[#include](/resource/relative.md) [\#include](/resource/relative.md)
# Multicolumn slides # Multicolumn slides
...@@ -87,7 +86,7 @@ The following text ist included from file `/resource/realtive.md`: ...@@ -87,7 +86,7 @@ The following text ist included from file `/resource/realtive.md`:
## Relative path ## Relative path
![](img/06-metal.png){width=75%} ![](img/06-metal.png){width="75%"}
# LaTeX Math # LaTeX Math
...@@ -209,9 +208,7 @@ $e=mc^2$ ...@@ -209,9 +208,7 @@ $e=mc^2$
# These are just notes {.notes} # These are just notes {.notes}
Slides with headers that are have the `.notes` class attribute are not Slides with headers that are have the `.notes` class attribute are not included in the presentation. They are only visible in the handout and probably are available as presenter notes during slide presentation.
included in the presentation. They are only visible in the handout and
probably are available as presenter notes during slide presentation.
# Cached Images # Cached Images
...@@ -219,8 +216,7 @@ probably are available as presenter notes during slide presentation. ...@@ -219,8 +216,7 @@ probably are available as presenter notes during slide presentation.
Remote images can be cached locally Remote images can be cached locally
Cache directory is named `img/cached` and is located in the directory of Cache directory is named `img/cached` and is located in the directory of the referencing document
the referencing document
`decker cache` scans for and downloads all images `decker cache` scans for and downloads all images
...@@ -228,8 +224,7 @@ the referencing document ...@@ -228,8 +224,7 @@ the referencing document
## Cached remote image ## Cached remote image
![Some piece of scene ![Some piece of scene graph](https://tramberend.beuth-hochschule.de/img/cg1-banner.png)
graph](https://tramberend.beuth-hochschule.de/img/cg1-banner.png)
# Meta Data # Meta Data
...@@ -298,8 +293,7 @@ Your total score is 42. ...@@ -298,8 +293,7 @@ Your total score is 42.
- `*-deck.html` a *reveal.js* based HTML slide deck - `*-deck.html` a *reveal.js* based HTML slide deck
- `*-deck.pdf` a PDF version of that deck - `*-deck.pdf` a PDF version of that deck
- `*-handout.html` a HTML document containing only the speaker notes - `*-handout.html` a HTML document containing only the speaker notes from the deck
from the deck
- `*-handout.pdf` a PDF version of that handout - `*-handout.pdf` a PDF version of that handout
## Generated from `*-page.md` ## Generated from `*-page.md`
...@@ -317,8 +311,7 @@ Your total score is 42. ...@@ -317,8 +311,7 @@ Your total score is 42.
## `decker` ## `decker`
- Recursively scans the current directory for Markdown files ending in - Recursively scans the current directory for Markdown files ending in `.md`
`.md`
## `decker clean` ## `decker clean`
......
Points: 5
LectureId: Some lecture id
Answer:
CorrectAnswer: THE ANSWER.
tag: FreeForm
HeightInMm: 20
TopicId: Some topic id
Title: FREE
Difficulty: Medium
Comment: COMMENT
Question: THE QUESTION?
Points: 5
LectureId: Some lecture id
Answer:
tag: FillText
CorrectWords:
- HOLES
- TEXT
FillText: FILL THE ___ IN THE ___.
TopicId: Some topic id
Title: FILL TEXT
Difficulty: Medium
Comment: COMMENT
Question: THE QUESTION?
Points: 5
LectureId: Some lecture id
Answer:
tag: MultipleChoice
Correct:
- ANSWER_1
- ANSWER_2
Incorrect:
- DISTRACTOR_1
- DISTRACTOR_2
TopicId: Some topic id
Title: MULTIPLE CHOICE
Difficulty: Medium
Comment: COMMENT
Question: THE QUESTION?
# Aufgabe N: {{Title}} # Aufgabe N: {{Title}}
| | | | | |
|---------------+-------------------| |---------------|----------------|
| Titel | **{{Title}}** | | Titel | **{{Title}}** |
| Id | {{Id}} | | Id | {{TopicId}} |
| Vorlesung | {{Lecture}} | | Vorlesung | {{LectureId}} |
| Schwierigkeit | {{Difficulty}} | | Schwierigkeit | {{Difficulty}} |
| Punkte | {{Points}} | | Punkte | {{Points}} |
| Kommentar | {{Comment}} | | Kommentar | {{Comment}} |
......
# Aufgabe N: {{Title}} # Aufgabe N: {{Title}}
| | | | | |
|---------------+-------------------| |---------------|----------------|
| Titel | **{{Title}}** | | Titel | **{{Title}}** |
| Id | {{Id}} | | Id | {{TopicId}} |
| Vorlesung | {{Lecture}} | | Vorlesung | {{LectureId}} |
| Schwierigkeit | {{Difficulty}} | | Schwierigkeit | {{Difficulty}} |
| Punkte | {{Points}} | | Punkte | {{Points}} |
| Kommentar | {{Comment}} | | Kommentar | {{Comment}} |
...@@ -13,4 +13,4 @@ ...@@ -13,4 +13,4 @@
{{Answer.FillText}} {{Answer.FillText}}
*Antwort:* {{#Answer.CorrectWords}}{{.}}, {{/Answer.CorrectWords}} *Antwort:* {{\#Answer.CorrectWords}}{{.}}, {{/Answer.CorrectWords}}
# decker # decker
A markdown based tool for slide deck creation. A markdown based tool for slide deck creation.
## Usage ## Usage
*decker* behaves very much like a build tool. It works recursively on the *decker* behaves very much like a build tool. It works recursively on the current directory and all subdirectories. Markdown files ending on `.md` in those directories are processed and converted to either a reveal.js slide show, a HTML document, or a PDF document, depending on the file name.
current directory and all subdirectories. Markdown files ending on `.md` in
those directories are processed and converted to either a reveal.js slide show,
a HTML document, or a PDF document, depending on the file name.
`*-deck.md` `*-deck.md`
: Files with this ending are processed as silde decks. From one source file : Files with this ending are processed as silde decks. From one source file potentially four different targets can be generated:
potentially four different targets can be generated:
- `*-deck.html` A reveal.js based slide show - `*-deck.html` A reveal.js based slide show
- `*-handout.hmtl` A HTML document containing the speaker notes to the - `*-handout.hmtl` A HTML document containing the speaker notes to the slide show.
slide show.
- `*-deck.pdf` A PDF version of the slide show - `*-deck.pdf` A PDF version of the slide show
- `*-handout.pdf` A PDF version of the handout - `*-handout.pdf` A PDF version of the handout
`*.md` `*-page.md`
: General Markdown files are translated into corresponding HTML or
PDF documents. : Markdown files ending on `*-page.md` are translated into corresponding HTML or PDF documents.
## *decker* targets ## *decker* targets
...@@ -34,69 +28,51 @@ decker html ...@@ -34,69 +28,51 @@ decker html
: Builds HTML versions of all available documents. : Builds HTML versions of all available documents.
decker pdf decker pdf
: Builds PDF versions of all documents that are generated from : Builds PDF versions of all documents that are generated from `*-deck.md` files.
`*-deck.md` files.
decker pdf-decks decker pdf-decks
: Builds PDF versions of all slide decks. : Builds PDF versions of all slide decks.
decker watch decker watch
: Builds HTML versions of all documents and then watches for document changes. : Builds HTML versions of all documents and then watches for document changes. Each change to a watched document triggers a rebuild. Watching can be terminated with `^C`.
Each change to a watched document triggers a rebuild. Watching can be
terminated with `^C`.
decker server decker server
: Like `decker watch`. Additionally a local web server is started that serves : Like `decker watch`. Additionally a local web server is started that serves the generated HTML files. The `index.html` document is automatically openend in the browser. Changed files are automatically reloaded in the browser.
the generated HTML files. The `index.html` document is automatically openend
in the browser. Changed files are automatically reloaded in the browser.
decker example decker example
: Write a few example files to the current directory. To start exploring
decker type
: Write a few example files to the current directory. To start exploring decker type
``` {.bash} ``` {.bash}
$ decker example $ decker example
$ decker server $ decker server
``` ```
and make some changes to the Markdown files. `example-deck.md` contains the and make some changes to the Markdown files. `example-deck.md` contains the source code for a slide deck that explains most of the features supported.
source code for a slide deck that explains most of the features supported.
decker clean decker clean
: Recursively removes all generated files from the current directory. : Recursively removes all generated files from the current directory.
decker check decker check
: Check for all required external depencies. If one of the programs is : Check for all required external depencies. If one of the programs is missing, an error is generated. Required programs include:
missing, an error is generated. Required programs include:
- `pdflatex` as part of a complete LaTeX installation - `pdflatex` as part of a complete LaTeX installation
- `decktape.sh` for the generation of PDF versions of slide decks - `decktape.sh` for the generation of PDF versions of slide decks
- `livereloadx` as live-reloading local webserver - `livereloadx` as live-reloading local webserver
- `rsync` to publish the documents to a remote location - `rsync` to publish the documents to a remote location