Commit 60505b6f authored by Matthieu Boileau's avatar Matthieu Boileau
Browse files

Turn into a Python module with quickstart command

parent 450f74dd
[submodule "reveal.js"]
path = reveal.js
url =
[submodule "skeleton/reveal.js"]
path = skeleton/reveal.js
url =
Build a small website to host Jupyter notebooks as course chapters
import argparse
from .nbcourse import get_config, build_site, DEFAULT_CONFIG_FILE
from .quickstart import quickstart
def main():
parser = argparse.ArgumentParser(description=__name__.__doc__)
config_group = parser.add_mutually_exclusive_group()
config_group.add_argument('--config', default=DEFAULT_CONFIG_FILE,
help='load YAML file containing site configuration')
quickstart_group = parser.add_mutually_exclusive_group()
quickstart_group.add_argument('--quickstart', action='store_true',
help='initiate a nbcourse directory')
args = parser.parse_args()
if args.quickstart:
config = get_config(args.config)
python -m nbcourse module entry point to run via python -m
from . import main
if __name__ == '__main__':
......@@ -13,20 +13,19 @@ 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"
DEFAULT_CONFIG_FILE = "nbcourse.yml"
THEME_DIR = 'theme/default'
TEMPLATE_PATH = Path(THEME_DIR, 'templates')
NB_DIR = 'notebooks'
def get_config(config_file=CONFIG_FILE):
def get_config(config_file=DEFAULT_CONFIG_FILE):
Parse yaml file to build the corresponding configuration dictionary
with open(config_file, 'r') as f:
config = yaml.safe_load(f)
config['configpath'] = Path(os.path.dirname(os.path.abspath(config_file)))
return config
......@@ -35,15 +34,14 @@ class HomePage:
html_template = "index.html"
def __init__(self, html, title=None, src=None, parent=None):
def __init__(self, html):
self.html = html
self.title = title
self.src = src
self.parent = parent
self.title = None
self.parent = None
self.variables = {}
self.chapters = {}
def get_chapter(self, path: Path):
def _get_chapter(self, path: Path):
"""Get chapter from notebook file (source: bookbook)"""
chapter_no = int(re.match(r'(\d+)\-', path.stem).group(1))
......@@ -64,16 +62,16 @@ class HomePage:
'title': header,
'filename': path.stem}
def get_variables(self, config: dict):
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.nbdir = config['configpath'] / 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 = self._get_chapter(nbfile)
chapter['preview_only'] = True if chapter['number'] \
in config['chapter_preview_only'] else False
......@@ -82,11 +80,14 @@ class HomePage:
print(">>> Homepage template variables:")
def render_template(self):
def _render_template(self, config):
"""Return html rendered from template and variables"""
# Inject variables into html_template
env = jinja2.Environment(loader=jinja2.FileSystemLoader(TEMPLATE_DIR))
full_template_path = config['configpath'] / TEMPLATE_PATH
print(full_template_path, os.path.exists(full_template_path.as_posix()))
env = jinja2.Environment(loader=jinja2.FileSystemLoader(
template = env.get_template(self.html_template)
html_out = template.render(self.variables)
with open(self.html, 'w') as f:
......@@ -94,22 +95,29 @@ class HomePage:
def render(self, config):
"""Render html using templating"""
class MarkdownPage(HomePage):
html_template = "article.html"
def render(self):
def __init__(self, html, title, src, parent=None):
self.title = title
self.src = Path(src)
self.parent = parent
def render(self, config):
"""Render markdown file into html file adding ToC"""
pattern = re.compile(r'^doc\/(.*)\.md$')
def markdown2html():
"""Return html from markdown file"""
with open(os.path.join(SCRIPT_PATH, self.src), 'r') as f:
with open(config['configpath'] / self.src, 'r') as f:
text =
md = markdown.Markdown(extensions=['fenced_code', 'codehilite',
......@@ -139,7 +147,7 @@ class MarkdownPage(HomePage):
'article_content': body,
'toc': toc,
'menu': get_menu_list()}
def build_site(config: dict):
......@@ -153,16 +161,5 @@ def build_site(config: dict):
anaconda = MarkdownPage('anaconda.html', title='Anaconda',
src='pages/', parent=manual)
if __name__ == '__main__':
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)
import os
from distutils.dir_util import copy_tree
SKEL_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
def quickstart():
copy_tree(SKEL_DIR, os.getcwd())
course_title := cours-python
config_file ?= nbcourse.yml
notebook_dir := notebooks
notebooks := $(wildcard $(notebook_dir)/*.ipynb)
output_dir := build
theme_dir := theme/default
makefile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
project_dir := $(dir $(makefile_path))
template_dir := $(project_dir)/$(theme_dir)/templates/
......@@ -36,7 +33,7 @@ help:
to run them without internet connection)"
@echo " pdf to compile all notebooks as a single PDF book"
@echo " archives to make files"
@echo " archive to make file"
@echo " archive to make $(course_title).zip file"
@echo " pages to make index and other pages"
@echo "Use \`make' to run all these targets"
......@@ -71,7 +68,7 @@ $(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_file)
cd $(output_dir) && python3 -m nbcourse --config ../$(config_file)
$(output_dir)/%.html: $(output_dir)/%.ipynb
$(call nbconvert,html,$<)
course_title := nbcourse
config_file ?= nbcourse.yml
notebook_dir := notebooks
output_dir := build
theme_dir := theme/default
\ No newline at end of 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
alt: Logo Python
- name: Matthieu Boileau
- name: Vincent Legoll
- 3
- 4
- 5
- title: Notice
target: manual.html
path: img/Infobox_info_icon.svg
width: 35px
- title: Exécuter
width: auto
- title: Version pdf
target: cours-python.pdf
path: img/Adobe_PDF_icon.svg
width: 30px
- title: Sources
path: img/GitLab_Logo.svg
width: 30px
- title: Archive complète
path: img/download.svg
width: 35px
text: Contenu mis à disposition sous licence
path: img/by-sa.svg
width: 70px
\ No newline at end of file
## Download Anaconda
Download Anaconda (<>) for your operating system and install it.
On Linux, open *Terminal* application and type:
cd Download
bash ./Anaconda*.sh
## Install the *Exercise2* extension for Jupyter
Use the Command line interface on:
- Windows : *Start menu > All programms > Anaconda3 (64bits) > Anaconda Prompt*
- Linux or Mac : open *Terminal*
Type :
conda install -c conda-forge jupyter_contrib_nbextensions
jupyter nbextension enable exercise2/main
## Execute the Jupyter notebooks
This course content is provided as Jupyter notebooks that require to be engined by a Jupyter server with Python3 kernel.
## Install Jupyter
### First install Anaconda
[Anaconda]( is complete and easy to install.
In particular, it is shipped with:
- [Jupyter](
- The [Spyder]( IDE
- Scipy libraries: Numpy, Pandas, etc.
For a detailed installation of Anaconda and its extensions on Windows, Mac or Linux, follow the instructions <a href="doc/"><img src="fig/anaconda.png" style="display:inline" alt="Anaconda logo" width="100px"></a>.
### Finalize installation with Pip
[Pip]( is required to install some packages that are not shipped with the Anaconda distribution.
From the project root directory, type:
pip install --user -r requirements.txt
jupyter contrib nbextension install --user
jupyter nbextension enable exercise2/main
## Run a Jupyter server
- Either from Anaconda graphical interface
- or the command line interface from the project root directory:
## Convert to html, slideshow and pdf
Use `make` :
make help
Please use `make <target>' where <target> is one of
html to make standalone HTML files
slides to make slideshows (use local_reveal=True to run them without internet connection)
pdf to compile all notebooks as a single PDF book
archives to make files
archive to make file
doc to make documentation
Use `make' to run all these targets
The result is located in `build/` directory.
## Publish with Pages
Thanks to [.gitlab-ci.yml](.gitlab-ci.yml) file, an online version may be automatically published at every `git push` towards a GitLab project.
Subproject commit 33bed47daca3f08c396215415e6ece005970734a
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