Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
79b5274
Fix grammar in version notes.
hodgestar Apr 4, 2024
846bb19
Replace prefix and suffix with Jinja escaping.
hodgestar Apr 4, 2024
250a755
Factor out loading the list of tutorials.
hodgestar Apr 4, 2024
277b58a
Make url_prefix a class variable rather than an accidental global one.
hodgestar Apr 4, 2024
0d96faf
Add notebook index template.
hodgestar Apr 4, 2024
ef7b9ef
Replace HTML lists with Markdown lists.
hodgestar Apr 4, 2024
1cf3b7d
Uppercase name of Notebook class.
hodgestar Apr 7, 2024
2166e20
Update create index to allow more control over what is indexed and wh…
hodgestar Apr 7, 2024
416c004
Merge branch 'main' into feature/generate-notebook-index
hodgestar Apr 20, 2024
b667458
Move raw after start of Markdown header.
hodgestar Apr 20, 2024
76617f4
Remove remaining HTML from notebook index.
hodgestar Apr 20, 2024
73289fd
Rename notebook index template to .notebook.jinja.
hodgestar Apr 20, 2024
28a11a0
Fix typo in root folder reference, update name of notebook index temp…
hodgestar Apr 20, 2024
bdcc406
Update HTML index creation to use new script.
hodgestar Apr 20, 2024
07e2736
Add option to generate Try QuTiP URLs.
hodgestar Apr 20, 2024
405db95
Update order of arguments to create index.
hodgestar Apr 20, 2024
45e77bf
Fix regular expression escape.
hodgestar Apr 3, 2025
0ef1be6
Merge branch 'main' into feature/generate-notebook-index
hodgestar Apr 3, 2025
fd1219f
Fix list formatting.
hodgestar Apr 3, 2025
240d69e
Update try-qutip template to follow the hierarchy of the usual tutori…
hodgestar Apr 3, 2025
b07d63f
Remove try-qutip URL hardcoding (jupyterlib seems happier with relati…
hodgestar Apr 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/notebook_ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ jobs:
cd ..
# build the website
python create_index.py
python create_index.py v5 html index.html
python create_index.py v4 html index-v4.html
bundle config path .gems
bundle install
bundle exec jekyll build
Expand Down
230 changes: 146 additions & 84 deletions website/create_index.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,60 @@
import os
""" Script for generating indexes of the notebook. """

import argparse
import pathlib
import re
from jinja2 import Environment, FileSystemLoader, select_autoescape

from jinja2 import (
Environment,
FileSystemLoader,
select_autoescape,
)


TUTORIAL_DIRECTORIES = [
'heom',
'lectures',
'pulse-level-circuit-simulation',
'python-introduction',
'quantum-circuits',
'time-evolution',
'visualization',
'miscellaneous'
]


def atoi(text):
return int(text) if text.isdigit() else text


def natural_keys(text):
return [atoi(c) for c in re.split('(\d+)', text)]
return [atoi(c) for c in re.split(r'(\d+)', text)]


class Notebook:
""" Notebook object for use in rendering templates. """

NBVIEWER_URL_PREFIX = "https://nbviewer.org/urls/qutip.org/qutip-tutorials/"

def __init__(self, title, tutorial_folder, path):
self.tutorial_folder = tutorial_folder
self.web_folder = tutorial_folder.parent

class notebook:
def __init__(self, path, title):
# remove ../ from path
self.path = path.replace('../', '')
self.title = title
# set url and update from markdown to ipynb
self.url = url_prefix + self.path.replace(".md", ".ipynb")

self.web_md_path = path.relative_to(self.web_folder)
self.web_ipynb_path = self.web_md_path.with_suffix(".ipynb")

self.tutorial_md_path = path.relative_to(self.tutorial_folder)
self.tutorial_ipynb_path = self.tutorial_md_path.with_suffix(".ipynb")

self.nbviewer_url = self.NBVIEWER_URL_PREFIX + self.web_ipynb_path.as_posix()
self.try_qutip_url = "./tutorials/" + self.tutorial_ipynb_path.as_posix()

def get_title(filename):

def get_title(path):
""" Reads the title from a markdown notebook """
with open(filename, 'r') as f:
with path.open('r') as f:
# get first row that starts with "# "
for line in f.readlines():
# trim leading/trailing whitespaces
Expand All @@ -36,98 +68,128 @@ def get_title(filename):
def sort_files_titles(files, titles):
""" Sorts the files and titles either by filenames or titles """
# identify numbered files and sort them
nfiles = [s for s in files if s.split('/')[-1][0].isdigit()]
nfiles = sorted(nfiles, key=natural_keys)
nfiles = [s for s in files if s.name[0].isdigit()]
nfiles = sorted(nfiles, key=lambda s: natural_keys(s.name))
ntitles = [titles[files.index(s)] for s in nfiles]

# sort the files without numbering by the alphabetic order of the titles
atitles = [titles[files.index(s)] for s in files if s not in nfiles]
atitles = sorted(atitles, key=natural_keys)
afiles = [files[titles.index(s)] for s in atitles]

# merge the numbered and unnumbered sorting
return nfiles + afiles, ntitles + atitles


def get_notebooks(path):
def get_notebooks(tutorials_folder, subfolder):
""" Gets a list of all notebooks in a directory """
# get list of files and their titles
try:
files = [path + f for f in os.listdir(path) if f.endswith('.md')]
except FileNotFoundError:
return {}
files = list((tutorials_folder / subfolder).glob("*.md"))
titles = [get_title(f) for f in files]
# sort the files and titles for display
files_sorted, titles_sorted = sort_files_titles(files, titles)
# generate notebook objects from the sorted lists and return
notebooks = [notebook(f, t) for f, t in zip(files_sorted, titles_sorted)]
notebooks = [
Notebook(title, tutorials_folder, path)
for title, path in zip(titles_sorted, files_sorted)
]
return notebooks


def generate_index_html(version_directory, tutorial_directories, title,
version_note):
""" Generates the index html file from the given data"""
# get tutorials from the different directories
def get_tutorials(tutorials_folder, tutorial_directories):
""" Return a dictionary of all tutorials for a particular version. """
tutorials = {}
for dir in tutorial_directories:
tutorials[dir] = get_notebooks(version_directory + dir + '/')
for subfolder in tutorial_directories:
tutorials[subfolder] = get_notebooks(tutorials_folder, subfolder)
return tutorials

# Load environment for Jinja and template

def render_template(template_path, **kw):
""" Render a Jinja template """
env = Environment(
loader=FileSystemLoader("../"),
autoescape=select_autoescape()
loader=FileSystemLoader(str(template_path.parent)),
autoescape=select_autoescape(),
)
template = env.get_template("website/index.html.jinja")

# render template and return
html = template.render(tutorials=tutorials, title=title,
version_note=version_note)
return html
template = env.get_template(template_path.name)
return template.render(**kw)


# url prefix for the links
url_prefix = "https://nbviewer.org/urls/qutip.org/qutip-tutorials/"
# tutorial directories
tutorial_directories = [
'heom',
'lectures',
'pulse-level-circuit-simulation',
'python-introduction',
'quantum-circuits',
'time-evolution',
'visualization',
'miscellaneous'
]
def parse_args():
parser = argparse.ArgumentParser(
description="""
Generate indexes for tutorial notebooks.

# +++ READ PREFIX AND SUFFIX +++
prefix = ""
suffix = ""

with open('prefix.html', 'r') as f:
prefix = f.read()
with open('suffix.html', 'r') as f:
suffix = f.read()

# +++ VERSION 4 INDEX FILE +++
title = 'Tutorials for QuTiP Version 4'
version_note = 'This are the tutorials for QuTiP Version 4. You can \
find the tutorials for QuTiP Version 5 \
<a href="./index.html">here</a>.'

html = generate_index_html('../tutorials-v4/', tutorial_directories, title,
version_note)
with open('index-v4.html', 'w+') as f:
f.write(prefix)
f.write(html)
f.write(suffix)

# +++ VERSION 5 INDEX FILE +++
title = 'Tutorials for QuTiP Version 5'
version_note = 'This are the tutorials for QuTiP Version 5. You can \
find the tutorials for QuTiP Version 4 \
<a href="./index-v4.html">here</a>.'

html = generate_index_html('../tutorials-v5/', tutorial_directories, title,
version_note)
with open('index.html', 'w+') as f:
f.write(prefix)
f.write(html)
f.write(suffix)
This script is used both by this repository to generate the indexes
for the QuTiP tutorial website and by https://github.com/qutip/try-qutip/
to generate the notebook indexes for the Try QuTiP site.
""",
)
parser.add_argument(
"qutip_version", choices=["v4", "v5"],
metavar="QUTIP_VERSION",
help="Which QuTiP version to generate the tutorial index for [v4, v5].",
)
parser.add_argument(
"index_type", choices=["html", "try-qutip"],
metavar="INDEX_TYPE",
help=(
"Whether to generate an HTML index for the website or"
" a Markdown Jupyter notebook index for the Try QuTiP site"
" [html, try-qutip]."
),
)
parser.add_argument(
"output_file",
metavar="OUTPUT_FILE",
help="File to write the index to.",
)
return parser.parse_args()


def main():
args = parse_args()

root_folder = pathlib.Path(__file__).parent.parent

if args.qutip_version == "v4":
title = "Tutorials for QuTiP Version 4"
tutorials_folder = root_folder / "tutorials-v4"
version_note = """
These are the tutorials for QuTiP Version 4. You can
find the tutorials for QuTiP Version 5
<a href="./index.html">here</a>.
""".strip()
elif args.qutip_version == "v5":
title = "Tutorials for QuTiP Version 5"
tutorials_folder = root_folder / "tutorials-v5"
version_note = """
These are the tutorials for QuTiP Version 5. You can
find the tutorials for QuTiP Version 4
<a href="./index-v4.html">here</a>.
""".strip()
else:
raise ValueError(f"Unsupported qutip_version: {args.qutip_version!r}")

tutorials = get_tutorials(tutorials_folder, TUTORIAL_DIRECTORIES)

if args.index_type == "html":
template = root_folder / "website" / "index.html.jinja"
text = render_template(
template,
title=title,
version_note=version_note,
tutorials=tutorials,
)
elif args.index_type == "try-qutip":
template = root_folder / "website" / "index.try-qutip.jinja"
text = render_template(
template,
title=title,
tutorials=tutorials,
)
else:
raise ValueError(f"Unsupported index_type: {args.index_type!r}")

with open(args.output_file, "w") as f:
f.write(text)


if __name__ == "__main__":
main()
28 changes: 20 additions & 8 deletions website/index.html.jinja
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
---
title: QuTiP Tutorials
---
{% raw %}
{% include header.html %}
{% include navbar.html %}
{% endraw %}

<div class="row">
<div class="col-md-12">
<h1>{{ title }}</h1><br>
Expand Down Expand Up @@ -60,7 +68,7 @@ useful to have a look at these IPython notebook
<h4 id="python-introduction">Python Introduction</h4>
<ul>
{% for item in tutorials['python-introduction'] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
<li><a href="{{ item.nbviewer_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
<p>For a more in depth discussion see: <a href="https://github.com/jrjohansson/scientific-python-lectures">Lectures on scientific computing with Python</a>.</p>
Expand All @@ -69,7 +77,7 @@ useful to have a look at these IPython notebook
<h4 id="visualizations">Visualization</h4>
<ul>
{% for item in tutorials['visualization'] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
<li><a href="{{ item.nbviewer_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>

Expand All @@ -79,14 +87,14 @@ useful to have a look at these IPython notebook
<h5 id="qip-circuits">Quantum circuits and algorithms</h5>
<ul>
{% for item in tutorials['quantum-circuits'] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
<li><a href="{{ item.nbviewer_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>

<h5 id="qip-pulse-level">Pulse-level circuit simulation</h5>
<ul>
{% for item in tutorials['pulse-level-circuit-simulation'] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
<li><a href="{{ item.nbviewer_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>

Expand All @@ -97,7 +105,7 @@ useful to have a look at these IPython notebook
<h4 id="time-evolution">Time evolution</h4>
<ul>
{% for item in tutorials['time-evolution'] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
<li><a href="{{ item.nbviewer_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>

Expand Down Expand Up @@ -137,14 +145,14 @@ useful to have a look at these IPython notebook
<h4 id="heom">Hierarchical Equations of Motion</h4>
<ul>
{% for item in tutorials['heom'] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
<li><a href="{{ item.nbviewer_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>

<h4 id="miscellaneous">Miscellaneous tutorials</h4>
<ul>
{% for item in tutorials['miscellaneous'] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
<li><a href="{{ item.nbviewer_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>

Expand All @@ -160,7 +168,7 @@ topics and analyze them numerically using QuTiP (some more detailed than others)

<ul>
{% for item in tutorials['lectures'] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
<li><a href="{{ item.nbviewer_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>

Expand All @@ -182,3 +190,7 @@ a complete archive of older versions of the tutorials is maintained there.

</div>
</div>

{% raw %}
{% include footer.html %}
{% endraw %}
Loading
Loading