1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PyYAML+Jinja2を使って雑な問題集を作る

Last updated at Posted at 2018-05-02

教育で使うかは別として、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

出力例

image.png

ここまでやったけど問題を起こすのがめんどくさくなってるので、使うかは不明。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?