1
- import os
1
+ """ Script for generating indexes of the notebook. """
2
+
3
+ import argparse
4
+ import pathlib
2
5
import re
3
- from jinja2 import Environment , FileSystemLoader , select_autoescape
6
+
7
+ from jinja2 import (
8
+ Environment ,
9
+ FileSystemLoader ,
10
+ select_autoescape ,
11
+ )
12
+
13
+
14
+ TUTORIAL_DIRECTORIES = [
15
+ 'heom' ,
16
+ 'lectures' ,
17
+ 'pulse-level-circuit-simulation' ,
18
+ 'python-introduction' ,
19
+ 'quantum-circuits' ,
20
+ 'time-evolution' ,
21
+ 'visualization' ,
22
+ 'miscellaneous'
23
+ ]
4
24
5
25
6
26
def atoi (text ):
7
27
return int (text ) if text .isdigit () else text
8
28
9
29
10
30
def natural_keys (text ):
11
- return [atoi (c ) for c in re .split ('(\d+)' , text )]
31
+ return [atoi (c ) for c in re .split (r'(\d+)' , text )]
32
+
12
33
34
+ class Notebook :
35
+ """ Notebook object for use in rendering templates. """
36
+
37
+ NBVIEWER_URL_PREFIX = "https://nbviewer.org/urls/qutip.org/qutip-tutorials/"
38
+
39
+ def __init__ (self , title , tutorial_folder , path ):
40
+ self .tutorial_folder = tutorial_folder
41
+ self .web_folder = tutorial_folder .parent
13
42
14
- class notebook :
15
- def __init__ (self , path , title ):
16
- # remove ../ from path
17
- self .path = path .replace ('../' , '' )
18
43
self .title = title
19
- # set url and update from markdown to ipynb
20
- self .url = url_prefix + self .path .replace (".md" , ".ipynb" )
21
44
45
+ self .web_md_path = path .relative_to (self .web_folder )
46
+ self .web_ipynb_path = self .web_md_path .with_suffix (".ipynb" )
47
+
48
+ self .tutorial_md_path = path .relative_to (self .tutorial_folder )
49
+ self .tutorial_ipynb_path = self .tutorial_md_path .with_suffix (".ipynb" )
50
+
51
+ self .nbviewer_url = self .NBVIEWER_URL_PREFIX + self .web_ipynb_path .as_posix ()
52
+ self .try_qutip_url = "./tutorials/" + self .tutorial_ipynb_path .as_posix ()
22
53
23
- def get_title (filename ):
54
+
55
+ def get_title (path ):
24
56
""" Reads the title from a markdown notebook """
25
- with open (filename , 'r' ) as f :
57
+ with path . open ('r' ) as f :
26
58
# get first row that starts with "# "
27
59
for line in f .readlines ():
28
60
# trim leading/trailing whitespaces
@@ -36,98 +68,128 @@ def get_title(filename):
36
68
def sort_files_titles (files , titles ):
37
69
""" Sorts the files and titles either by filenames or titles """
38
70
# identify numbered files and sort them
39
- nfiles = [s for s in files if s .split ( '/' )[ - 1 ] [0 ].isdigit ()]
40
- nfiles = sorted (nfiles , key = natural_keys )
71
+ nfiles = [s for s in files if s .name [0 ].isdigit ()]
72
+ nfiles = sorted (nfiles , key = lambda s : natural_keys ( s . name ) )
41
73
ntitles = [titles [files .index (s )] for s in nfiles ]
74
+
42
75
# sort the files without numbering by the alphabetic order of the titles
43
76
atitles = [titles [files .index (s )] for s in files if s not in nfiles ]
44
77
atitles = sorted (atitles , key = natural_keys )
45
78
afiles = [files [titles .index (s )] for s in atitles ]
79
+
46
80
# merge the numbered and unnumbered sorting
47
81
return nfiles + afiles , ntitles + atitles
48
82
49
83
50
- def get_notebooks (path ):
84
+ def get_notebooks (tutorials_folder , subfolder ):
51
85
""" Gets a list of all notebooks in a directory """
52
- # get list of files and their titles
53
- try :
54
- files = [path + f for f in os .listdir (path ) if f .endswith ('.md' )]
55
- except FileNotFoundError :
56
- return {}
86
+ files = list ((tutorials_folder / subfolder ).glob ("*.md" ))
57
87
titles = [get_title (f ) for f in files ]
58
- # sort the files and titles for display
59
88
files_sorted , titles_sorted = sort_files_titles (files , titles )
60
- # generate notebook objects from the sorted lists and return
61
- notebooks = [notebook (f , t ) for f , t in zip (files_sorted , titles_sorted )]
89
+ notebooks = [
90
+ Notebook (title , tutorials_folder , path )
91
+ for title , path in zip (titles_sorted , files_sorted )
92
+ ]
62
93
return notebooks
63
94
64
95
65
- def generate_index_html (version_directory , tutorial_directories , title ,
66
- version_note ):
67
- """ Generates the index html file from the given data"""
68
- # get tutorials from the different directories
96
+ def get_tutorials (tutorials_folder , tutorial_directories ):
97
+ """ Return a dictionary of all tutorials for a particular version. """
69
98
tutorials = {}
70
- for dir in tutorial_directories :
71
- tutorials [dir ] = get_notebooks (version_directory + dir + '/' )
99
+ for subfolder in tutorial_directories :
100
+ tutorials [subfolder ] = get_notebooks (tutorials_folder , subfolder )
101
+ return tutorials
72
102
73
- # Load environment for Jinja and template
103
+
104
+ def render_template (template_path , ** kw ):
105
+ """ Render a Jinja template """
74
106
env = Environment (
75
- loader = FileSystemLoader ("../" ),
76
- autoescape = select_autoescape ()
107
+ loader = FileSystemLoader (str ( template_path . parent ) ),
108
+ autoescape = select_autoescape (),
77
109
)
78
- template = env .get_template ("website/index.html.jinja" )
79
-
80
- # render template and return
81
- html = template .render (tutorials = tutorials , title = title ,
82
- version_note = version_note )
83
- return html
110
+ template = env .get_template (template_path .name )
111
+ return template .render (** kw )
84
112
85
113
86
- # url prefix for the links
87
- url_prefix = "https://nbviewer.org/urls/qutip.org/qutip-tutorials/"
88
- # tutorial directories
89
- tutorial_directories = [
90
- 'heom' ,
91
- 'lectures' ,
92
- 'pulse-level-circuit-simulation' ,
93
- 'python-introduction' ,
94
- 'quantum-circuits' ,
95
- 'time-evolution' ,
96
- 'visualization' ,
97
- 'miscellaneous'
98
- ]
114
+ def parse_args ():
115
+ parser = argparse .ArgumentParser (
116
+ description = """
117
+ Generate indexes for tutorial notebooks.
99
118
100
- # +++ READ PREFIX AND SUFFIX +++
101
- prefix = ""
102
- suffix = ""
103
-
104
- with open ('prefix.html' , 'r' ) as f :
105
- prefix = f .read ()
106
- with open ('suffix.html' , 'r' ) as f :
107
- suffix = f .read ()
108
-
109
- # +++ VERSION 4 INDEX FILE +++
110
- title = 'Tutorials for QuTiP Version 4'
111
- version_note = 'This are the tutorials for QuTiP Version 4. You can \
112
- find the tutorials for QuTiP Version 5 \
113
- <a href="./index.html">here</a>.'
114
-
115
- html = generate_index_html ('../tutorials-v4/' , tutorial_directories , title ,
116
- version_note )
117
- with open ('index-v4.html' , 'w+' ) as f :
118
- f .write (prefix )
119
- f .write (html )
120
- f .write (suffix )
121
-
122
- # +++ VERSION 5 INDEX FILE +++
123
- title = 'Tutorials for QuTiP Version 5'
124
- version_note = 'This are the tutorials for QuTiP Version 5. You can \
125
- find the tutorials for QuTiP Version 4 \
126
- <a href="./index-v4.html">here</a>.'
127
-
128
- html = generate_index_html ('../tutorials-v5/' , tutorial_directories , title ,
129
- version_note )
130
- with open ('index.html' , 'w+' ) as f :
131
- f .write (prefix )
132
- f .write (html )
133
- f .write (suffix )
119
+ This script is used both by this repository to generate the indexes
120
+ for the QuTiP tutorial website and by https://github.com/qutip/try-qutip/
121
+ to generate the notebook indexes for the Try QuTiP site.
122
+ """ ,
123
+ )
124
+ parser .add_argument (
125
+ "qutip_version" , choices = ["v4" , "v5" ],
126
+ metavar = "QUTIP_VERSION" ,
127
+ help = "Which QuTiP version to generate the tutorial index for [v4, v5]." ,
128
+ )
129
+ parser .add_argument (
130
+ "index_type" , choices = ["html" , "try-qutip" ],
131
+ metavar = "INDEX_TYPE" ,
132
+ help = (
133
+ "Whether to generate an HTML index for the website or"
134
+ " a Markdown Jupyter notebook index for the Try QuTiP site"
135
+ " [html, try-qutip]."
136
+ ),
137
+ )
138
+ parser .add_argument (
139
+ "output_file" ,
140
+ metavar = "OUTPUT_FILE" ,
141
+ help = "File to write the index to." ,
142
+ )
143
+ return parser .parse_args ()
144
+
145
+
146
+ def main ():
147
+ args = parse_args ()
148
+
149
+ root_folder = pathlib .Path (__file__ ).parent .parent
150
+
151
+ if args .qutip_version == "v4" :
152
+ title = "Tutorials for QuTiP Version 4"
153
+ tutorials_folder = root_folder / "tutorials-v4"
154
+ version_note = """
155
+ These are the tutorials for QuTiP Version 4. You can
156
+ find the tutorials for QuTiP Version 5
157
+ <a href="./index.html">here</a>.
158
+ """ .strip ()
159
+ elif args .qutip_version == "v5" :
160
+ title = "Tutorials for QuTiP Version 5"
161
+ tutorials_folder = root_folder / "tutorials-v5"
162
+ version_note = """
163
+ These are the tutorials for QuTiP Version 5. You can
164
+ find the tutorials for QuTiP Version 4
165
+ <a href="./index-v4.html">here</a>.
166
+ """ .strip ()
167
+ else :
168
+ raise ValueError (f"Unsupported qutip_version: { args .qutip_version !r} " )
169
+
170
+ tutorials = get_tutorials (tutorials_folder , TUTORIAL_DIRECTORIES )
171
+
172
+ if args .index_type == "html" :
173
+ template = root_folder / "website" / "index.html.jinja"
174
+ text = render_template (
175
+ template ,
176
+ title = title ,
177
+ version_note = version_note ,
178
+ tutorials = tutorials ,
179
+ )
180
+ elif args .index_type == "try-qutip" :
181
+ template = root_folder / "website" / "index.try-qutip.jinja"
182
+ text = render_template (
183
+ template ,
184
+ title = title ,
185
+ tutorials = tutorials ,
186
+ )
187
+ else :
188
+ raise ValueError (f"Unsupported index_type: { args .index_type !r} " )
189
+
190
+ with open (args .output_file , "w" ) as f :
191
+ f .write (text )
192
+
193
+
194
+ if __name__ == "__main__" :
195
+ main ()
0 commit comments