教育で使うかは別として、YAMLから問題集を起こすPythonスクリプトを書きました。(ただのテンプレート出力ですが)
やりたいこと
- 印刷でいい感じに出したい、メールで送れるようにしたい
- HTMLファイルで出す。1ファイルで収まるようにする。
- (CSSフレームワークとかJavaScriptのライブラリはCDNで公開されているものを利用)
- 回答は隠したい
- テンプレートで出し分ける。HTMLだからJavaScriptで頑張る方法もあるが...
- 問題の管理にDBを使いたくない
- yamlでよさそう
実装
こんな感じで使うライブラリを決めました
- yamlで問題を定義できたら楽そう
- jinja2はansibleでも使ってるので馴染みがあるのと、yamlで読んだ結果をそのまま流せそう
- markdownでyamlの問題が書けたら少し便利そう。
思い付き - HTMLならJavaScriptのhighlightjsでシンタックスハイライト入れられる。
思い付き
入力データ例
「大項目」と「問題」の2階層を想定してます。
traning.yml
---
- title: ディレクトリの移動・操作
quizzes:
- question: |-
現在の作業中のディレクトリ(ワーキングディレクトリ)のパスを表示するコマンドは何か
answer: |-
~~~{.bash}
pwd
~~~
- question: |-
現在のディレクトリから`/tmp`に移動する操作を書きなさい
answer: |-
~~~{.bash}
cd /tmp
~~~
※|-
を使うと、複数行かけてよい。(末端の改行は削除される。)
生成コード
generater.py
import yaml
import markdown
import jinja2
import sys, io
# always utf-8 encoding.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
# markdown extension
MARKDOWN_EXTENSIONS = ['markdown.extensions.extra']
# add filter (markdown)
loader = jinja2.FileSystemLoader('./templates')
env = jinja2.Environment(autoescape=True, loader=loader)
env.filters['markdown'] = lambda text: markdown.markdown(text, MARKDOWN_EXTENSIONS)
# render
tranings = yaml.load(sys.stdin.buffer)
template : jinja2.Template = env.get_template("traning.j2")
html = template.render(tranings = tranings)
# output
print(html)
工夫したところは、markdownの変換処理をjinja2のフィルタで書くようにしたところです。
テンプレート例
回答を隠したいときは<div class="quiz-answer">
あたりを削れば良いです。
templates/tranings.j2
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>とれーにんぐ</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB"
crossorigin="anonymous">
<style>
.quiz-content{
margin-left: 1em;
}
.quiz-answer::before{
content: "A."
}
</style>
<script>
window.addEventListener('load', function(){
Array.from(document.querySelectorAll('pre code')).forEach(function(el){
hljs.highlightBlock(el);
})
})
</script>
</head>
<body>
<div class="container-fluid">
<section>
<ul>
{% for traning in tranings %} {% set traning_loop = loop %}
<li>
<div><a href="#{{traning.title}}">{{ traning_loop.index }}.{{traning.title}}</a></div>
<ul>
{% for quiz in traning.quizzes %}
<li><a href="#quiz-{{ traning_loop.index }}-{{ loop.index }}">{{ traning_loop.index }}.{{ loop.index }}. {{quiz.question}}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</section>
{% for traning in tranings %} {% set traning_loop = loop %}
<section class="traning">
<h2 id="{{ traning.title }}">{{ traning_loop.index }}. {{ traning.title }}</h2>
{% for quiz in traning.quizzes %}
<div class="quiz">
<h3 id="quiz-{{ traning_loop.index }}-{{ loop.index }}" class="quiz-no">Q. {{ traning_loop.index }}-{{ loop.index }}.</h3>
<div class="quiz-content">
<div class="quiz-question">
{{ quiz.question | markdown | safe }}
</div>
<div class="quiz-answer">
{{ quiz.answer | markdown | safe }}
</div>
</div>
</div>
{% endfor %}
</section>
{% endfor %}
</div>
</body>
使用例
python generator.py < traning.yml > output.html
出力例
ここまでやったけど問題を起こすのがめんどくさくなってるので、使うかは不明。