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
let metaA = globA "**/*-meta.yaml"
-- Read meta data.
-- metaData <- readMetaDataIO meta
-- Calculate targets
let decksA = deckSourcesA >>= calcTargets ".md" ".html"
let decksPdfA = deckSourcesA >>= calcTargets ".md" ".pdf"
......
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad ()
import Control.Exception
import Data.Maybe ()
......@@ -27,115 +29,153 @@ import Test
import Embed
main :: IO ()
main =
do
-- Calculate some directories
projectDir <- calcProjectDirectory
let privateDir = projectDir </> "private"
-- Find sources
testFiles <- glob "**/*-quest.yaml"
-- Meta data
metaFiles <- glob "**/*-meta.yaml"
-- Calculate targets
let catalog = privateDir </> "complete-quest-catalog.pdf"
-- Prepare Mustache templates
let templates = compileTesterTemplates
---
shakeArgs shakeOptions $
do want ["catalog"]
--
catalog %>
\out ->
do need testFiles
allQuestions <- readTests testFiles
renderCatalog projectDir templates allQuestions out
--
phony "catalog" $
do need [catalog]
--
phony "new-mc" $
do let string = Y.encodePretty Y.defConfig multipleChoiceStationary
liftIO $ B.writeFile "new-mc-quest.yaml" string
--
phony "new-ft" $
do let string = Y.encodePretty Y.defConfig fillTextStationary
liftIO $ B.writeFile "new-ft-quest.yaml" string
--
phony "new-f" $
do let string = Y.encodePretty Y.defConfig freeStationary
liftIO $ B.writeFile "new-f-quest.yaml" string
--
phony "clean" $
do removeFilesAfter "." ["private"]
main = do
-- Calculate some directories
projectDir <- calcProjectDirectory
let privateDir = projectDir </> "private"
-- Find questions
questionFiles <- glob "**/*-quest.yaml"
-- Find exams
examFiles <- glob "**/*-exam.yaml"
-- Meta data
metaFiles <- glob "**/*-meta.yaml"
-- Calculate targets
let catalog = privateDir </> "complete-quest-catalog.pdf"
-- Prepare Mustache templates
let templates = compileTesterTemplates
---
shakeArgs
shakeOptions $
do want ["catalog"]
--
catalog %>
\out ->
do need questionFiles
allQuestions <- readTests questionFiles
renderCatalog projectDir templates allQuestions out
--
phony
"catalog" $
do need [catalog]
--
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
liftIO $ B.writeFile "new-mc-quest.yaml" string
--
phony
"new-ft" $
do let string = Y.encodePretty Y.defConfig fillTextStationary
liftIO $ B.writeFile "new-ft-quest.yaml" string
--
phony
"new-f" $
do let string = Y.encodePretty Y.defConfig freeStationary
liftIO $ B.writeFile "new-f-quest.yaml" string
--
phony
"clean" $
do removeFilesAfter "." ["private"]
-- Reads all the questions and returns them along with the base directory of
-- each.
readTests :: [FilePath] -> Action [(Question, FilePath)]
readTests
:: [FilePath] -> Action [(Question, FilePath)]
readTests files = mapM readTest files
where readTest :: FilePath -> Action (Question, FilePath)
readTest file =
do absolutePath <- liftIO $ makeAbsolute file
string <- liftIO $ B.readFile absolutePath
let question =
case Y.decodeEither' string of
Right yaml -> yaml
Left exception ->
throw $
YamlException $
"Error parsing YAML file: " ++
file ++ ", " ++ (show exception)
return (question, takeDirectory absolutePath)
where
readTest :: FilePath -> Action (Question, FilePath)
readTest file = do
absolutePath <- liftIO $ makeAbsolute file
string <- liftIO $ B.readFile absolutePath
let question =
case Y.decodeEither' string of
Right yaml -> yaml
Left exception ->
throw $
YamlException $
"Error parsing YAML file: " ++ file ++ ", " ++ (show exception)
return (question, takeDirectory absolutePath)
renderCatalog :: 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
need $ concatMap extractLocalImagePathes pandoc
let catalog =
Pandoc nullMeta $ concatMap (\(Pandoc _ blocks) -> blocks) pandoc
let options =
def {writerStandalone = True
,writerTemplate = B.unpack testLatexTemplate
,writerHighlight = True
,writerHighlightStyle = pygments
,writerCiteMethod = Citeproc}
putNormal $ "# pandoc (for " ++ out ++ ")"
pandocMakePdf options catalog out
where parseMarkdown (markdown,base) =
case readMarkdown def markdown of
-- Renders a catalog of all questions (TODO sorted by LectureId and TopicId).
renderCatalog
:: 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
need $ concatMap extractLocalImagePathes pandoc
let catalog =
Pandoc nullMeta $
concatMap
(\(Pandoc _ blocks) ->
blocks)
pandoc
let options =
def
{ writerStandalone = True
, writerTemplate = B.unpack testLatexTemplate
, writerHighlight = True
, writerHighlightStyle = pygments
, writerCiteMethod = Citeproc
}
putNormal $ "# pandoc (for " ++ out ++ ")"
pandocMakePdf options catalog out
where
parseMarkdown (markdown,base) =
case readMarkdown def markdown of
Left err -> throw $ PandocException (show err)
Right pandoc -> walk (adjustImageUrls base) pandoc
adjustImageUrls base (Image attr inlines (url,title)) =
(Image attr inlines (adjustLocalUrl projectDir base url,title))
adjustImageUrls _ inline = inline
renderMarkdown question =
T.unpack $
M.substitute (selectTemplate templates question)
(MT.mFromJSON question)
adjustImageUrls base (Image attr inlines (url,title)) =
(Image attr inlines (adjustLocalUrl projectDir base url, title))
adjustImageUrls _ inline = inline
renderMarkdown question =
T.unpack $ M.substitute (selectTemplate templates question) (MT.mFromJSON question)
-- TODO Make this work
newOrder :: Int -> IO [Int]
newOrder n =
do gen <- getStdGen
return $ shuffle' [0..(n-1)] n gen
newOrder
:: Int -> IO [Int]
newOrder n = do
gen <- getStdGen
return $ shuffle' [0 .. (n - 1)] n gen
-- TODO Make this work
shuffleAnswers :: Question -> Action Question
shuffleAnswers q =
case qstAnswer q of
MultipleChoice choices correct ->
do let n = length choices
order <- liftIO $ newOrder n
return q {qstAnswer =
MultipleChoice (shuffle choices order)
(shuffle correct order)}
otherwise -> return q
shuffleAnswers
:: Question -> Action Question
shuffleAnswers q =
case qstAnswer q of
MultipleChoice choices correct -> do
let n = length choices
order <- liftIO $ newOrder n
return
q
{ qstAnswer = MultipleChoice (shuffle choices order) (shuffle correct order)
}
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 =
multipleChoiceStationary =
Question
{ qstId = "ID"
, qstLecture = 0
{ qstTopicId = "TOPIC_ID"
, qstLectureId = "LECTURE_ID"
, qstTitle = "MULTIPLE CHOICE"
, qstPoints = 5
, qstQuestion = "THE QUESTION?"
......@@ -148,10 +188,10 @@ multipleChoiceStationary =
}
fillTextStationary :: Question
fillTextStationary =
fillTextStationary =
Question
{ qstId = "ID"
, qstLecture = 0
{ qstTopicId = "TOPIC_ID"
, qstLectureId = "LECTURE_ID"
, qstTitle = "FILL TEXT"
, qstPoints = 5
, qstQuestion = "THE QUESTION?"
......@@ -164,10 +204,10 @@ fillTextStationary =
}
freeStationary :: Question
freeStationary =
freeStationary =
Question
{ qstId = "ID"
, qstLecture = 0
{ qstTopicId = "TOPIC_ID"
, qstLectureId = "LECTURE_ID"
, qstTitle = "FREE"
, qstPoints = 5
, qstQuestion = "THE QUESTION?"
......@@ -177,4 +217,4 @@ freeStationary =
}
, qstDifficulty = Medium
, qstComment = "COMMENT"
}
}
\ No newline at end of file
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
## Pandoc-Markdown
- Slides are basically [Pandoc-Markdown](http://pandoc.org) formatted
text
- Slides are basically [Pandoc-Markdown](http://pandoc.org) formatted text
- Pandoc provides a Markdown variant with many extensions
## Some Pandoc extensions
......@@ -55,7 +54,7 @@ transition: linear
The following text ist included from file `/resource/realtive.md`:
[#include](/resource/relative.md)
[\#include](/resource/relative.md)
# Multicolumn slides
......@@ -63,7 +62,7 @@ The following text ist included from file `/resource/realtive.md`:
![](img/htr-beuth.jpg)
###
###
## Slide source
......@@ -87,7 +86,7 @@ The following text ist included from file `/resource/realtive.md`:
## Relative path
![](img/06-metal.png){width=75%}
![](img/06-metal.png){width="75%"}
# LaTeX Math
......@@ -118,7 +117,7 @@ $$
[:youtube](Wji-BZ0oCwg)
```
###
###
## Video
......@@ -167,7 +166,7 @@ $$
- This block is marked `.alert`
```
###
###
## Block styles
......@@ -194,7 +193,7 @@ $e=mc^2$
These are speaker notes.
```
###
###
## Block level
......@@ -209,9 +208,7 @@ $e=mc^2$
# These are just notes {.notes}
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.
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.
# Cached Images
......@@ -219,17 +216,15 @@ probably are available as presenter notes during slide presentation.
Remote images can be cached locally
Cache directory is named `img/cached` and is located in the directory of
the referencing document
Cache directory is named `img/cached` and is located in the directory of the referencing document
`decker cache` scans for and downloads all images
###
###
## Cached remote image
![Some piece of scene
graph](https://tramberend.beuth-hochschule.de/img/cg1-banner.png)
![Some piece of scene graph](https://tramberend.beuth-hochschule.de/img/cg1-banner.png)
# Meta Data
......@@ -298,8 +293,7 @@ Your total score is 42.
- `*-deck.html` a *reveal.js* based HTML slide deck
- `*-deck.pdf` a PDF version of that deck
- `*-handout.html` a HTML document containing only the speaker notes
from the deck
- `*-handout.html` a HTML document containing only the speaker notes from the deck
- `*-handout.pdf` a PDF version of that handout
## Generated from `*-page.md`
......@@ -317,8 +311,7 @@ Your total score is 42.
## `decker`
- Recursively scans the current directory for Markdown files ending in
`.md`
- Recursively scans the current directory for Markdown files ending in `.md`
## `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}}
| | |
|---------------+-------------------|
| | |
|---------------|----------------|
| Titel | **{{Title}}** |
| Id | {{Id}} |
| Vorlesung | {{Lecture}} |
| Id | {{TopicId}} |
| Vorlesung | {{LectureId}} |
| Schwierigkeit | {{Difficulty}} |
| Punkte | {{Points}} |
| Kommentar | {{Comment}} |
......
# Aufgabe N: {{Title}}
| | |
|---------------+-------------------|
| | |
|---------------|----------------|
| Titel | **{{Title}}** |
| Id | {{Id}} |
| Vorlesung | {{Lecture}} |
| Id | {{TopicId}} |
| Vorlesung | {{LectureId}} |
| Schwierigkeit | {{Difficulty}} |
| Punkte | {{Points}} |
| Kommentar | {{Comment}} |
......@@ -13,4 +13,4 @@
{{Answer.FillText}}
*Antwort:* {{#Answer.CorrectWords}}{{.}}, {{/Answer.CorrectWords}}
*Antwort:* {{\#Answer.CorrectWords}}{{.}}, {{/Answer.CorrectWords}}
# decker
# decker
A markdown based tool for slide deck creation.
## Usage
*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.
*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.
`*-deck.md`
: Files with this ending are processed as silde decks. From one source file
potentially four different targets can be generated:
: Files with this ending are processed as silde decks. From one source file potentially four different targets can be generated:
- `*-deck.html` A reveal.js based slide show
- `*-handout.hmtl` A HTML document containing the speaker notes to the
slide show.
- `*-handout.hmtl` A HTML document containing the speaker notes to the slide show.
- `*-deck.pdf` A PDF version of the slide show
- `*-handout.pdf` A PDF version of the handout
`*.md`
: General Markdown files are translated into corresponding HTML or
PDF documents.
`*-page.md`
: Markdown files ending on `*-page.md` are translated into corresponding HTML or PDF documents.
## *decker* targets
......@@ -34,69 +28,51 @@ decker html
: Builds HTML versions of all available documents.
decker pdf
: Builds PDF versions of all documents that are generated from
`*-deck.md` files.
: Builds PDF versions of all documents that are generated from `*-deck.md` files.
decker pdf-decks
: Builds PDF versions of all slide decks.
decker watch
: 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`.
: 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`.
decker server
: 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.
: 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.
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}
$ decker example
$ decker server
```
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.
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.
decker clean
: Recursively removes all generated files from the current directory.
decker check
: Check for all required external depencies. If one of the programs is
missing, an error is generated. Required programs include:
: Check for all required external depencies. If one of the programs is missing, an error is generated. Required programs include:
- `pdflatex` as part of a complete LaTeX installation
- `decktape.sh` for the generation of PDF versions of slide decks
- `livereloadx` as live-reloading local webserver
- `rsync` to publish the documents to a remote location
decker source
decker plan
: Prints a list of all source files found below the current directory.
decker meta
: Pretty prints all meta data that can be found in `*.yaml` files in the
current directory and below. Meta data is mainly used to perform
substitutions in Markdown documents using the Mustache templating system.
: Pretty prints all meta data that can be found in `*.yaml` files in the current directory and below. Meta data is mainly used to perform substitutions in Markdown documents using the Mustache templating system.
decker publish
: Publish the generated files to a remote location using `rsync` if the
location is specified in the meta data. The keys `rsync-destination.host`
and `rsync-destination.path` specify the publishing destination.
decker cache
: Remote images referenced in the Markdown documents are downloaded to a local
cache directory. If a local copy of a remote image exists in the chache, it
is used in all subsequent document builds.
: Publish the generated files to a remote location using `rsync` if the location is specified in the meta data. The keys `rsync-destination.host` and `rsync-destination.path` specify the publishing destination.
decker clean-cache
: Remove all cached image files. Subsequent document builds will use the
original remote images.
: Remove all cached image files. Subsequent document builds will use the original remote images.
## Installation
......
# Aufgabe N: {{Title}}
| | |
|---------------+-------------------|
| | |
|---------------|----------------|
| Titel | **{{Title}}** |
| Id | {{Id}} |
| Vorlesung | {{Lecture}} |
| Id | {{TopicId}} |
| Vorlesung | {{LectureId}} |
| Schwierigkeit | {{Difficulty}} |
| Punkte | {{Points}} |
| Kommentar | {{Comment}} |
......@@ -13,9 +13,14 @@
*Antworten:*
{{#Answer.Correct}}