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

Solve #8 (metadata in md files)

parent dd107137
......@@ -17,7 +17,10 @@ import nbformat
from nbconvert.preprocessors import ExecutePreprocessor
from nbconvert import HTMLExporter, SlidesExporter
from .mydoit import MyDoitMain, ClassTaskLoader
import logging
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
DEFAULT_CONFIG_FILE = "nbcourse.yml"
......@@ -36,13 +39,13 @@ class NbCourse:
'pages': {
'dir': 'pages',
'titlepage': 'titlepage.tex',
'home': {'name': 'index.html'}
'home': 'index.html'
},
'bookbook_filename': 'bookbook.tex',
'local_reveal': False,
'slug_title': 'course',
'output_dir': 'build',
'title': 'A course'
'title': 'A course',
}
def __init__(self, config_file: Path = None):
......@@ -70,7 +73,7 @@ class NbCourse:
'.pdf')
self.notebooks = tuple(self.conf['nb']['path'].glob('*-*.ipynb'))
self.book = self.conf['output_path'] / self.conf['book_title']
self.md_pages = list(self.conf['pages']['path'].glob('*.md'))
self.md_page_paths = list(self.conf['pages']['path'].glob('*.md'))
self.html_pages = self._get_pages() # homepage and documentation pages
self.theme_files = [file for file in
self.conf['theme']['path'].glob('**/*')
......@@ -100,12 +103,10 @@ class NbCourse:
def _get_pages(self):
"""get a list of target html pages from markdown source"""
# Start html_pages list with index.html
html_pages = [self.conf['output_path'] /
self.conf['pages']['home']['name']]
for md_page in self.md_pages:
html_pages = [self.conf['output_path'] / self.conf['pages']['home']]
for path in self.md_page_paths:
html_pages.append(self.conf['output_path'] /
md_page.relative_to(
self.conf['pages']['path']).with_suffix('.html'))
Path(path.name).with_suffix('.html'))
return html_pages
@staticmethod
......@@ -121,28 +122,27 @@ class NbCourse:
Build a mini website using html templating and markdown conversion
"""
def render_children(parent, parent_page):
# List mardkown files
md_page_paths = list(self.conf['pages']['path'].glob('*.md'))
# Instanciate all markdown pages to be rendered
md_pages = [MarkdownPage(self, md_page_path)
for md_page_path in md_page_paths]
def render_children(parent):
"""Recursive function to render children pages from parent page"""
try:
children = parent['children']
for child in children.values():
# Render markdown pages
child_page = MarkdownPage(self, child['html'],
title=child['title'],
src=child['md'],
parent=parent_page)
child_page.render()
render_children(child, child_page)
except KeyError:
# Parent page has no children
return
children = (md_page for md_page in md_pages
if md_page.parent_name == parent.name)
for child in children:
# Render markdown pages
child.render(parent)
render_children(child)
# First build homepage
homepage = HomePage(self)
homepage.render()
# Then build children pages
render_children(self.conf['pages']['home'], homepage)
render_children(homepage)
def build_book(self):
"""Build a pdf book using bookbook"""
......@@ -297,7 +297,7 @@ class NbCourse:
def task_build_pages(self):
"""Build html pages"""
return {
'file_dep': self.md_pages + self.theme_files,
'file_dep': self.md_page_paths + self.theme_files,
'task_dep': ['copy_material'],
'targets': self.html_pages,
'clean': True,
......
......@@ -6,23 +6,53 @@ from pathlib import Path
from pprint import pformat
import jinja2
from bs4 import BeautifulSoup
import frontmatter
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
class HomePage:
"""A class to handle a homepage to be rendered"""
class Page:
"""An abstract class for a static web page"""
html_template = "index.html"
html_template = ''
html = ''
def __init__(self, nbcourse):
conf = nbcourse.conf.copy()
self.html = conf['output_path'] / conf['pages']['home']['name']
self.html_path = conf['output_path'] / self.html
self.title = conf['title']
self.notebooks = nbcourse.notebooks
self.parent = None
self.variables = conf
self.template_path = conf['template_path']
self.variables = conf
self.notebooks = nbcourse.notebooks
def _render_template(self):
"""Return html rendered from template and variables"""
# Inject variables into html_template
env = jinja2.Environment(loader=jinja2.FileSystemLoader(
self.template_path.as_posix()))
template = env.get_template(self.html_template)
html_out = template.render(self.variables)
with open(self.html_path, 'w') as f:
f.write(html_out)
def render(self):
"""Render html using templating"""
raise NotImplementedError()
class HomePage(Page):
"""A class to handle a homepage to be rendered"""
html_template = "index.html"
html = "index.html"
name = 'home'
parent = None
parent_name = ''
def __init__(self, nbcourse):
super().__init__(nbcourse)
self.chapters = {}
@staticmethod
......@@ -49,7 +79,6 @@ class HomePage:
def _get_variables(self):
"""Set some variables from conf dictionnary and notebooks files"""
logging.basicConfig(level=logging.INFO)
# Get chapters from notebook files
chapters = []
......@@ -63,8 +92,8 @@ class HomePage:
chapters.append(chapter)
chapters.sort(key=lambda chapter: chapter['number'])
self.variables.update({'chapters': chapters})
log.info("Homepage template variables:")
log.info(pformat(self.variables))
log.debug("Homepage template variables:")
log.debug(pformat(self.variables))
def _render_template(self):
"""Return html rendered from template and variables"""
......@@ -74,7 +103,7 @@ class HomePage:
self.template_path.as_posix()))
template = env.get_template(self.html_template)
html_out = template.render(self.variables)
with open(self.html, 'w') as f:
with open(self.html_path, 'w') as f:
f.write(html_out)
def render(self):
......@@ -83,29 +112,46 @@ class HomePage:
self._render_template()
class MarkdownPage(HomePage):
class MarkdownPage(Page):
html_template = "article.html"
def __init__(self, nbcourse, html, title, src, parent=None):
def __init__(self, nbcourse, src: Path):
super().__init__(nbcourse)
self.html = nbcourse.conf['output_path'] / html
self.title = title
self.src = nbcourse.conf['pages']['path'] / Path(src)
self.parent = parent
def render(self):
self.src = src
self.html = Path(self.src.name).with_suffix('.html')
self.html_path = nbcourse.conf['output_path'] / self.html
with open(self.src, 'r') as md_file:
metadata, self.md_content = frontmatter.parse(md_file.read())
self.title = metadata['title']
self.parent_name = metadata.get('parent', 'home')
self.name = self.src.name
self.parent = None # to be populate later
def get_menu_list(self):
"""Return a list to be displayed as a top menu"""
# At least current page
menu = [(self.title, self.html)]
page = self # Initialize with current page
# Ascend to parent page
while page.parent.parent:
menu.append((page.parent.title, page.parent.html))
page = page.parent
menu.reverse()
return menu
def render(self, parent):
"""Render markdown file into html file adding ToC"""
self.parent = parent
pattern = re.compile(r'^doc\/(.*)\.md$')
def markdown2html():
"""Return html from markdown file"""
with open(self.src, 'r') as f:
text = f.read()
md = markdown.Markdown(extensions=['fenced_code', 'codehilite',
'toc'])
body = md.convert(text)
body = md.convert(self.md_content)
# Transform link to file.md into link to file.html
soup = BeautifulSoup(body, 'html.parser')
......@@ -115,20 +161,9 @@ class MarkdownPage(HomePage):
return soup.prettify(), md.toc
def get_menu_list():
"""Return a list to be displayed as a top menu"""
# At least current page
menu = [(self.title, self.html)]
page = self
while page.parent.parent:
menu.append((page.parent.title, page.parent.html))
page = page.parent
menu.reverse()
return menu
body, toc = markdown2html()
self.variables = {'title': self.title,
'article_content': body,
'toc': toc,
'menu': get_menu_list()}
'menu': self.get_menu_list()}
self._render_template()
......@@ -7,4 +7,5 @@ latex
markdown
rise
pyyaml
doit
\ No newline at end of file
doit
python-frontmatter
\ No newline at end of file
......@@ -47,17 +47,4 @@ license:
icon:
path: img/by-sa.svg
width: 70px
pages:
home:
html: index.html
children:
manual:
html: manual.html
md: manual.md
title: Notice
children:
anaconda:
html: anaconda.html
md: anaconda.md
title: Anaconda
local_reveal: True
---
title: Anaconda
parent: manual.md
---
## Download
[Download Anaconda](https://www.anaconda.com/download) for your operating system and install it.
......
---
title: Manual
parent: home
---
## Execute the Jupyter notebooks
This course content is provided as Jupyter notebooks that require to be engined by a Jupyter server with Python3 kernel.
......
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