Commit adc196d7 authored by Matthieu Boileau's avatar Matthieu Boileau
Browse files

Build chapters from notebooks

parent e9a74f69
Pipeline #5195 failed with stage
in 20 seconds
course_title := cours-python
config_file ?= nbcourse.yml
notebook_dir := notebooks
notebooks := $(wildcard $(notebook_dir)/*.ipynb)
output_dir := build
......@@ -69,9 +70,8 @@ copy_reveal: $(output_dir)
$(executed_notebooks): $(output_dir)/%.ipynb: $(notebook_dir)/%.ipynb
$(call nbconvert,notebook,$<) --execute --allow-errors --ExecutePreprocessor.timeout=60
$(pages): $(pages_md) $(wildcard $(theme_dir)/img/*) $(wildcard $(theme_dir)/css/*) $(wildcard $(theme_dir)/templates/*) $(CONFIG)
cd $(output_dir) && python3 $(project_dir)/ --config $(CONFIG)
cd $(output_dir) && python3 $(project_dir)/ --config $(config_file)
$(output_dir)/%.html: $(output_dir)/%.ipynb
$(call nbconvert,html,$<)
#!/usr/bin/env python3
Build a small website to host Jupyter notebooks as course chapter
Build a small website to host Jupyter notebooks as course chapters
import argparse
import os
from pathlib import Path
import jinja2
import yaml
from pprint import pprint
import markdown
from bs4 import BeautifulSoup
import re
import nbformat
SCRIPT_PATH = os.path.relpath(os.path.join(os.path.dirname(__file__)))
THEME_DIR = os.path.join(SCRIPT_PATH, 'theme', 'default')
TEMPLATE_DIR = os.path.join(THEME_DIR, 'templates')
CONFIG_FILE = "nbcourse.yml"
NB_DIR = 'notebooks'
def get_config(config_file=CONFIG_FILE):
......@@ -22,8 +26,8 @@ def get_config(config_file=CONFIG_FILE):
Parse yaml file to build the corresponding configuration dictionary
with open(config_file, 'r') as f:
variables = yaml.safe_load(f)
return variables
config = yaml.safe_load(f)
return config
class HomePage:
......@@ -37,31 +41,60 @@ class HomePage:
self.src = src
self.parent = parent
self.variables = {}
def get_variables(self, variables_file):
Parse yaml file to build the corresponding configuration dictionary
with open(variables_file, 'r') as f:
variables = yaml.safe_load(f)
self.title = variables['title']
self.variables = variables
self.chapters = {}
def get_chapter(self, path: Path):
"""Get chapter from notebook file (source: bookbook)"""
chapter_no = int(re.match(r'(\d+)\-', path.stem).group(1))
nb =, as_version=4)
assert nb.cells[0].cell_type == 'markdown', nb.cells[0].cell_type
lines = nb.cells[0].source.splitlines()
if lines[0].startswith('# '):
header = lines[0][2:]
elif len(lines) > 1 and lines[1].startswith('==='):
header = lines[0]
assert False, "No heading found in {}".format(path)
assert path.suffix == '.ipynb', path
return {'number': chapter_no,
'title': header,
'filename': path.stem}
def get_variables(self, config: dict):
"""Set some variables from config dictionnary and notebooks files"""
self.title = config['title']
self.nbdir = Path(SCRIPT_PATH) / Path(config.get('nbdir', NB_DIR))
self.variables = config
# Get chapters from notebook files
chapters = []
for nbfile in self.nbdir.glob('*-*.ipynb'):
chapter = self.get_chapter(nbfile)
chapter['preview_only'] = True if chapter['number'] \
in config['chapter_preview_only'] else False
chapters.sort(key=lambda chapter: chapter['number'])
self.variables.update({'chapters': chapters})
print(">>> Homepage template variables:")
def render_template(self):
"""Return html rendered from template and variables"""
# Inject html table, title and comment into html_template
# Inject variables into html_template
env = jinja2.Environment(loader=jinja2.FileSystemLoader(TEMPLATE_DIR))
template = env.get_template(self.html_template)
html_out = template.render(self.variables)
with open(self.html, 'w') as f:
def render(self, variables_file):
def render(self, config):
"""Render html using templating"""
......@@ -109,11 +142,11 @@ class MarkdownPage(HomePage):
def build_doc(variables_file):
"""Build documentation using html templating and markdown conversion"""
def build_site(config: dict):
"""Build a mini website using html templating and markdown conversion"""
# Render homepage
homepage = HomePage('index.html')
# Render markdown pages
manual = MarkdownPage('manual.html', title='Notice', src='pages/',
......@@ -125,11 +158,11 @@ def build_doc(variables_file):
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=build_doc.__doc__)
parser.add_argument('--config', metavar='filename',
parser = argparse.ArgumentParser(description=__name__.__doc__)
parser.add_argument('--config', default=CONFIG_FILE,
help='YAML file containing site configuration')
args = parser.parse_args()
config_file = os.path.join(SCRIPT_PATH, args.config)
config = get_config(config_file)
title: Apprendre Python pour les sciences
subtitle: Master Communication Scientifique, 2019-2020
favicon: fig/favicon.ico
nbdir: notebooks
path: fig/python-logo_full.png
width: 300px
......@@ -10,20 +11,10 @@ authors:
- name: Vincent Legoll
- title: Les généralités
filename: 01-generalites
- title: Le langage (1)
filename: 02-langage
- title: Le langage (2)
filename: 03-langage
preview_only: True
- title: Microprojet
filename: 04-microprojet
preview_only: True
- title: Pandas
filename: 05-pandas
preview_only: True
- 3
- 4
- 5
- title: Notice
target: manual.html
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment