Commit 95aac1aa authored by Matthieu Boileau's avatar Matthieu Boileau

Initial commit

parents
*.swp
.DS_Store
.ipynb_checkpoints
*.pyc
__pycache__/
.idea/
.vscode/
venv/
build/
notebooks/
\ No newline at end of file
variables:
GIT_SUBMODULE_STRATEGY: recursive
pages:
variables:
COURSE_GITLAB_URL: $CI_PROJECT_URL
tags:
- docker
- pages
only:
variables:
- $CI_COMMIT_REF_NAME == $CI_PROJECT_NAMESPACE
- $CI_COMMIT_REF_NAME == /^pub-.*$/
image: boileaum/jupyter
script:
- make clean
- make install
- make -j 7 VARIABLES=$CI_PROJECT_NAMESPACE.yml
- make archive
- mv build public
artifacts:
paths:
- public
[submodule "reveal.js"]
path = reveal.js
url = https://github.com/hakimel/reveal.js.git
Copyright <YEAR> <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
course_title := cours-python
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/
local_reveal := False
ifeq ($(local_reveal),True)
# Useful for running in local with internet connexion
revealprefix := "reveal.js"
else
# Needed for online publication by gitlab pages
revealprefix := "https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.3.0"
endif
executed_notebooks := $(addprefix $(output_dir)/, $(notdir $(notebooks)))
html := $(addprefix $(output_dir)/, $(notdir $(subst .ipynb,.html,$(notebooks))))
slides := $(addprefix $(output_dir)/, $(notdir $(subst .ipynb,.slides.html,$(notebooks))))
archives := $(addprefix $(output_dir)/, $(subst .ipynb,.zip,$(notdir $(notebooks))))
pages_md := $(wildcard pages/*.md)
pages := $(output_dir)/index.html\
$(addprefix $(output_dir)/, $(notdir $(subst .md,.html,$(pages_md))))
.PHONY: all clean html slides executed_notebooks pages copy_to_build pdf archives archive install
all: $(output_dir) html slides pages archives pdf
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " slides to make slideshows (use local_reveal=True \
to run them without internet connection)"
@echo " pdf to compile all notebooks as a single PDF book"
@echo " archives to make ##-notebook.zip files"
@echo " archive to make course_title.zip file"
@echo " pages to make index and other pages"
@echo "Use \`make' to run all these targets"
install:
pip install -U --user -r requirements.txt
executed_notebooks: copy_to_build $(executed_notebooks)
html: copy_to_build $(html)
slides: copy_reveal $(slides)
pages: copy_to_build $(pages)
pdf: copy_to_build $(output_dir)/$(course_title).pdf
archives: $(output_dir) $(archives)
archive: $(output_dir)/$(course_title).zip
define nbconvert
jupyter nbconvert --to $(1) $< --output-dir=$(output_dir)
endef
$(output_dir):
@mkdir -p $(output_dir)
copy_to_build: $(output_dir)
rsync -ra --delete $(notebook_dir)/fig $(output_dir)/ --exclude ".*/" --exclude "__pycache__"
rsync -ra --delete $(notebook_dir)/exos $(output_dir)/ --exclude ".*/" --exclude "__pycache__"
rsync -ra --delete $(theme_dir)/css $(output_dir)/
rsync -ra --delete $(theme_dir)/img $(output_dir)/
copy_reveal: $(output_dir)
rsync -ra --delete reveal.js $(output_dir)/
$(executed_notebooks): $(output_dir)/%.ipynb: $(notebook_dir)/%.ipynb
$(call nbconvert,notebook,$<) --execute --allow-errors --ExecutePreprocessor.timeout=60
CONFIG?=nbcourse.yml
$(pages): $(pages_md) $(wildcard $(theme_dir)/img/*) $(wildcard $(theme_dir)/css/*) $(wildcard $(theme_dir)/templates/*) $(CONFIG)
cd $(output_dir) && python3 $(project_dir)/nbcourse.py --config $(CONFIG)
$(output_dir)/%.html: $(output_dir)/%.ipynb
$(call nbconvert,html,$<)
$(output_dir)/%.slides.html: $(output_dir)/%.ipynb
$(call nbconvert,slides,$<) --reveal-prefix $(revealprefix)
$(output_dir)/$(course_title).tex: executed_notebooks $(template_dir)/book.tplx
cd $(output_dir) && python3 -m bookbook.latex --output-file $(course_title) --template $(template_dir)/book.tplx
$(output_dir)/$(course_title).pdf: $(executed_notebooks) $(template_dir)/book.tplx
cd $(output_dir) && python3 -m bookbook.latex --pdf --output-file $(course_title) --template $(template_dir)/book.tplx
$(output_dir)/%.zip: $(notebook_dir)/%.ipynb
zip -r $@ $< $(notebook_dir)/fig $(notebook_dir)/exos --exclude "*/\.*" "*__pycache__*"
$(output_dir)/$(course_title).zip: all
cd $(output_dir) && zip -r $(course_title).zip * --exclude "*/\.*" "*__pycache__*" "*.e" "*.zip"
clean:
rm -rf $(output_dir)
#!/usr/bin/env python3
"""
Build a small website to host Jupyter notebooks as course chapter
"""
import argparse
import os
import jinja2
import yaml
from pprint import pprint
import markdown
from bs4 import BeautifulSoup
import re
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"
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
class HomePage:
"""A class to handle a homepage to be rendered"""
html_template = "index.html"
def __init__(self, html, title=None, src=None, parent=None):
self.html = html
self.title = title
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
print(">>> Homepage template variables:")
pprint(variables)
def render_template(self):
"""Return html rendered from template and variables"""
# Inject html table, title and comment 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:
f.write(html_out)
def render(self, variables_file):
"""Render html using templating"""
self.get_variables(variables_file)
self.render_template()
class MarkdownPage(HomePage):
html_template = "article.html"
def render(self):
"""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:
text = f.read()
md = markdown.Markdown(extensions=['fenced_code', 'codehilite',
'toc'])
body = md.convert(text)
# Transform link to file.md into link to file.html
soup = BeautifulSoup(body, 'html.parser')
for link in soup.findAll(['a']):
href = link.get('href')
link['href'] = pattern.sub(r'\1.html', href)
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()}
self.render_template()
def build_doc(variables_file):
"""Build documentation using html templating and markdown conversion"""
# Render homepage
homepage = HomePage('index.html')
homepage.render(variables_file)
# Render markdown pages
manual = MarkdownPage('manual.html', title='Notice', src='pages/manual.md',
parent=homepage)
anaconda = MarkdownPage('anaconda.html', title='Anaconda',
src='pages/anaconda.md', parent=manual)
anaconda.render()
manual.render()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=build_doc.__doc__)
parser.add_argument('--config', metavar='filename',
default=CONFIG_FILE,
help='YAML file containing site configuration')
args = parser.parse_args()
config_file = os.path.join(SCRIPT_PATH, args.config)
build_doc(config_file)
title: Apprendre Python pour les sciences
subtitle: Master Communication Scientifique, 2019-2020
favicon: fig/favicon.ico
picture:
path: fig/python-logo.png
width: 300px
alt: Logo Python
authors:
- name: Matthieu Boileau
email: matthieu.boileau@math.unistra.fr
- name: Vincent Legoll
email: vincentlegoll@gmail.com
chapters:
- 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
links:
- title: Notice
target: manual.html
icon:
path: img/Infobox_info_icon.svg
width: 35px
- title: Exécuter
target: https://mybinder.org/v2/gh/fitzinger/cours-python/master
icon:
path: https://mybinder.org/badge.svg
width: auto
- title: Version pdf
target: cours-python.pdf
icon:
path: img/Adobe_PDF_icon.svg
width: 30px
- title: Sources
target: https://gitlab.math.unistra.fr/fitzinger/cours-python
icon:
path: img/GitLab_Logo.svg
width: 30px
- title: Archive complète
target: cours-python.zip
icon:
path: img/download.svg
width: 35px
license:
text: Contenu mis à disposition sous licence
target: https://creativecommons.org/licenses/by-sa/4.0/
icon:
path: img/by-sa.svg
width: 70px
\ No newline at end of file
../cours-python
\ No newline at end of file
## Installer Anaconda
Télécharger Anaconda (<https://www.anaconda.com/download>) pour le système d'exploitation visé et l'installer.
Pour Linux, ouvrir l'application *Terminal* :
```bash
cd Téléchargements
bash ./Anaconda*.sh
```
## Installer l'extension *Exercise2* pour Jupyter
### Soit en ligne de commande
- Windows : *Menu démarrer > Tous les programmes > Anaconda3 (64bits) > Anaconda Prompt*
- Linux ou Mac : ouvrir *Terminal*
Taper :
```bash
conda install -c conda-forge jupyter_contrib_nbextensions
jupyter nbextension enable exercise2/main
```
### Soit par l'interface graphique
#### Installer *jupyter_contrib_nbextensions*
1. Ouvrir Anaconda Navigator :
- Mac : *Applications/Anaconda-Navigator*
- Linux ou Mac : dans Terminal taper `~/anaconda3/bin/anaconda-navigator`
- Windows : *Menu démarrer > Tous les programmes > Anaconda3 (64bits) > Anaconda Navigator*
1. Menu de gauche : *Environments > base (root) > Channels* puis *Add* : entrer `conda-forge` puis *Update channels*
1. Menu déroulant *Installed* : sélectionner *All*
1. Dans *Search packages*, entrer `jupyter_contrib_nbextensions`
1. Cocher la case devant *jupyter_contrib_nbextensions* puis *Apply* et *Apply* à nouveau dans la fenêtre popup
#### Installer l'extension *Exercise2*
1. Menu de gauche *Home > Jupyter notebook > Launch*
1. Dans un navigateur, aller sur <http://localhost:8888/nbextensions> ou depuis la page d'accueil du notebook sélectionner l'onglet de droite *Nbextensions*
1. Cocher la case *Exercise2*
## Exécuter les notebooks Jupyter
Le contenu de ce cours est fourni sous forme de notebooks Jupyter qui nécessitent d'être exécutés par un serveur Jupyter équipé d'un noyau Python 3.
### Solution 1 : utiliser Mybinder
<a href="https://mybinder.org/v2/gh/fitzinger/cours-python/master"><img src="https://mybinder.org/badge.svg" style="display:inline"></a> permet d'exécuter en ligne le contenu des notebooks mais la durée d'utilisation est limitée et **le stockage n'est pas persistant** (les données sont perdues si elles ne sont pas téléchargées avant la fin de l'exécution du serveur Mybinder).
### Solution 2 : utiliser un serveur Jupyter local
#### Installer Jupyter
##### Installation initiale avec Anaconda
[Anaconda](https://www.anaconda.com/distribution) est une suite assez complète et facile à utiliser qui contient entre autres :
- [Jupyter](http://jupyter.org/)
- l'IDE [Spyder](https://github.com/spyder-ide/spyder)
- les bibliothèques de Scipy : Numpy, Pandas, etc.
Pour une installation détaillée d'Anaconda et des extensions sur Windows, Mac ou Linux, suivre la notice <a href="doc/anaconda.md"><img src="fig/anaconda.png" style="display:inline" width="100px"></a>.
##### Installation finale avec Pip
[Pip](https://pypi.python.org/pypi/pip) est nécessaire pour installer certains paquets qui ne sont pas dans la distribution Anaconda.
Depuis le répertoire racine du projet, taper :
```bash
pip install --user -r requirements.txt
jupyter contrib nbextension install --user
jupyter nbextension enable exercise2/main
```
#### Lancer un serveur Jupyter
- Soit via l'interface d'Anaconda
- Soit en ligne de commande depuis le répertoire racine du projet :
```
jupyter-notebook
```
## Convertir les notebooks en html, diaporama et pdf
Utiliser `make` :
```bash
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 ##-notebook.zip files
archive to make cours-python.zip file
doc to make documentation
Use `make' to run all these targets
```
Le résultat se trouve dans le répertoire `build/`.
## Suivre avec Git
Pour éviter des différences indésirables dans git liées aux exécutions des cellules du notebook, on peut utiliser `nbstripout` :
```bash
pip install --upgrade nbstripout
# Depuis le répertoire racine du projet :
nbstripout install
```
## Publier avec Pages
Grâce au fichier [.gitlab-ci.yml](.gitlab-ci.yml), la [version en ligne](https://fitzinger.pages.math.unistra.fr/cours-python) est publiée automatiquement par GitLab Pages à chaque `git push` vers le dépôt GitLab.
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
overflow: visible;
padding: 8px;
background-color: #fff;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
overflow: visible;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.42857143;
color: #000;
background-color: #fff;
margin: 0;
display: block;
background-color: #fff;
min-height: 0;
}
.container {
margin-right: auto;
margin-left: auto;
margin-top: 1em;
margin-bottom: 1em;
box-shadow: 0px 0px 12px 1px rgba(87, 87, 87, 0.2);
padding: 4em 5em 4em 5em;
}
@media (min-width: 1200px){
.container {
width: 1140px;
}
}
@media (min-width: 992px){
.container {
width: 940px;
}
}
@media (min-width: 768px){
.container {
width: 768px;
}
}
p {
padding: 10px;
}
h1, h2, h3, h4, h5 {
font-weight: bold;
line-height: 1.0;
padding: 10px;
}
h1 {
font-size: 185.7%;
margin: 1.08em 0 0 0;
}
h2 {
font-size: 157.1%;
margin: 1.4em 0 0 0;
line-height: 1.5;
}
h3 {
font-size: 128.6%;
margin: 1.27em 0 0 0;
}
h4, h5, h6 {
font-size: 100%;
}
h4 {
margin: 1em 0 0 0;
}
h5, h6 {
margin: 1em 0 0 0;
font-style: italic;
}
h5 {
padding-left: 30px;
}
h6 {
padding-left: 60px;
}
hr {
margin-top: 18px;
margin-bottom: 18px;
border: 0;
border-top: 1px solid #eeeeee;
}
table {
margin-left: auto;
margin-right: auto;
border-spacing: 0;
color: black;
table-layout: fixed;
display: inline;
background-color: transparent;
}
table {
display: inline;
}
tr, th, td {
padding: 10px;
}
tr#td {
font-weight: normal !important;
font-size: 100%;
}
th {
font-weight: normal;
font-size: 90%;
border-bottom: 1px solid #ddd !important;
}
th#chap {
font-size: 140%;
font-weight: bold;
text-align: left;
}
td {
border: 0 !important;
font-size: 120%;
}
img {
display: block;
margin: auto;
}
#bottom td{
font-size: 120%;
text-align: center;
}
#auteurs td{
font-size: 100% !important;
padding-top: 0px !important;
}
#auteurs img{
vertical-align: middle;
}
#license {
font-style: italic;
}
#license img {
vertical-align: middle;
display:inline;
}
code, .codehilite pre {
background-color: #f8f8f8;
border-radius: 3px;
}
code, tt {
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
border: 1px solid #eaeaea;
color: #333;
}
.codehilite pre {
border: 1px solid #ccc;