environment doesn't seem to work with CSS sheet
367 | # For plaintext blocks
368 | html = html.replace('', '')
369 | # For inline highlighting
370 | html = html.replace('', '')
371 |
372 | # Another hack since \n is converted to [space] in links
373 | html = html.replace('%0A"','%20"')
374 |
375 | # Add the favicon
376 | html = html.replace('',
377 | '\n')
378 |
379 | return html
380 |
381 | def notebook2slides(filename):
382 | '''
383 | Converts notebook file to a slide show
384 | '''
385 | slides_exp = nc.SlidesExporter()
386 | slides_exp.reveal_scroll = True # Doesn't work?
387 | slides, resources = slides_exp.from_filename(filename)
388 |
389 | # Custom CSS is in the directory above slides
390 | slides = slides.replace('href="custom.css"', 'href="../custom.css"')
391 |
392 | # Replace '.ipynb' in links with '.html'
393 | # the '"' ensures this (hopefully) only happens in links
394 | slides = slides.replace('.ipynb"', '.html"')
395 | slides = slides.replace('.ipynb#', '.html#')
396 |
397 | # Horrible hack because environment doesn't seem to work with CSS sheet
398 | # For plaintext blocks
399 | slides = slides.replace('', '')
400 | # For inline highlighting
401 | slides = slides.replace('', '')
402 |
403 | # Another hack since \n is converted to [space] in links
404 | slides = slides.replace('%0A"','%20"')
405 |
406 | # Add the favicon
407 | slides = slides.replace('',
408 | '\n')
409 |
410 | return slides
411 |
412 | def directory_contents(directory):
413 | '''
414 | Returns directory notebook contents
415 | split into lessons and solutions
416 | '''
417 | # Store contents of directory as list
418 | contents = os.listdir(directory)
419 | contents.sort()
420 | try:
421 | # Remove checkpoints folder from list
422 | contents.remove('.ipynb_checkpoints')
423 | except ValueError:
424 | pass
425 |
426 | # Removes everything that isn't a notebook ending with .ipynb
427 | contents = [f for f in contents if '.ipynb' in f]
428 |
429 | # Remove solution files from contents and store in seperate list
430 | soln_contents = [f for f in contents if '-soln' in f]
431 | contents = [f for f in contents if '-soln' not in f]
432 |
433 | return contents, soln_contents
434 |
435 | def navigation_triple(directory, inputfile):
436 | '''
437 | Given a directory and file determines which file is
438 | - previous lesson
439 | - schedule
440 | - next lesson
441 | and returns these files as a dict
442 | '''
443 | contents, _ = directory_contents(directory)
444 |
445 | contents.append(contents[0])
446 |
447 | current = inputfile.split('/')[-1]
448 | # Exceptional case if you're making a new solution document
449 | if '-soln' in current:
450 | current = current.replace('-soln','')
451 |
452 | index = contents.index(current)
453 |
454 | outdir = './'
455 | triple = { 'previous' : outdir+contents[index-1],
456 | 'index' : outdir+contents[0],
457 | 'next' : outdir+contents[index+1] }
458 | return triple
459 |
460 |
--------------------------------------------------------------------------------
/nbfancy/__main__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import sys
4 | import argparse
5 | import pkg_resources
6 |
7 | import nbformat as nf
8 | import nbconvert as nc
9 |
10 | from shutil import move, copy, copytree
11 | from distutils.dir_util import copy_tree # Prefer copy_tree due to overwrite
12 | from . import globalconf
13 | from . import nbfancy_tools as nbftools
14 |
15 | # Trivial function for testing functionality
16 | hello = lambda x: print('Hello World!')
17 |
18 | def init(args):
19 | '''
20 | Initialises a directory
21 | '''
22 | parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
23 | parser.prog += ' ' + sys.argv[1]
24 | parser.add_argument('dir',
25 | type=str,
26 | default=os.getcwd(),
27 | nargs='?',
28 | help='Directory to initialise')
29 | parser.add_argument('--extra_conf',
30 | action='store_true',
31 | help='Initialise additional configuration files')
32 | parser.add_argument('--include',
33 | choices=['tutorial', 'template', 'none'],
34 | default = 'template',
35 | help='Fill nbplain directory with examples')
36 | args, unknown = parser.parse_known_args(sys.argv[2:])
37 |
38 | # Get cwd
39 | cwd = args.dir
40 |
41 | if not os.path.isdir(cwd):
42 | try:
43 | os.mkdir(cwd)
44 | except FileExistsError:
45 | print('Directory', cwd, 'already exists')
46 |
47 | # Create standard directories
48 | # Except nbplain, which is created at the end
49 | dir_list = ['config', 'images', 'code', 'data']
50 | for idir in dir_list:
51 | make_dir = os.path.join(cwd, idir)
52 | if not os.path.isdir(make_dir):
53 | os.mkdir(make_dir)
54 |
55 | # Our default configuration data
56 | resource_package = 'nbfancy'
57 | config_path = '/config' # Do not use os.path.join()
58 | config_source = pkg_resources.resource_filename(resource_package, config_path)
59 |
60 | # Setup config files
61 | config_dir = os.path.join(cwd, 'config')
62 | config_files = ['header.ipynb', 'footer.ipynb']
63 | if args.extra_conf:
64 | config_files += ['box.ipynb', 'keywords.cfg']
65 |
66 | for ifile in config_files:
67 | source = os.path.join(config_source, ifile)
68 | target = os.path.join(config_dir, ifile)
69 | copy(source, target)
70 |
71 | # Add a .gitignore to exclude nbfancy and html directories
72 | # as well as checkpoints
73 | ignore_path = pkg_resources.resource_filename(resource_package, '/tools')
74 | ignore_source = os.path.join(ignore_path, 'example_gitignore')
75 | ignore_target = os.path.join(cwd, '.gitignore')
76 | copy(ignore_source, ignore_target)
77 |
78 | # Copy template if specified
79 | if args.include != 'none': # works for template and tutorial
80 | template_path = '/' + args.include # Do not use os.path.join()
81 | source = pkg_resources.resource_filename(resource_package, template_path)
82 | target = os.path.join(cwd, 'nbplain')
83 | copy_tree(source, target)
84 |
85 | else: # works for none
86 | make_dir = os.path.join(cwd, 'nbplain')
87 | if not os.path.isdir(make_dir):
88 | try:
89 | os.mkdir(make_dir)
90 | except FileExistsError:
91 | print('Directory', make_dir, 'already exists')
92 |
93 |
94 | def configure(args):
95 | '''
96 | Sets up global config
97 | '''
98 | parser = argparse.ArgumentParser()
99 | parser.prog += ' ' + sys.argv[1]
100 | parser.add_argument('package',
101 | choices=['jupyter_css', 'bash2_magic', 'pdflatex_magic', 'all_magic'],
102 | help='additional package to configure for use with nbfancy')
103 | parser.add_argument('-y',
104 | action='store_true',
105 | help='answer yes to config question (only for scripting)')
106 | args, unknown = parser.parse_known_args(sys.argv[2:])
107 |
108 | if not args.y:
109 | print('This step necessarily changes your user\'s global iPython or Jupyter config.')
110 | print('This will affect the system copy of these configuration files, even if you are in a virtualenv')
111 | user_input = input('Do you wish to proceed? [Y/N] : ')
112 | if 'y' in user_input.lower():
113 | confirmed = True
114 | else:
115 | confirmed = False
116 | else:
117 | confirmed = True
118 |
119 | if confirmed:
120 | if args.package == 'jupyter_css':
121 | globalconf.jupyter()
122 | elif args.package == 'all_magic':
123 | globalconf.ipython()
124 | else:
125 | print('You can\'t pick and choose, just install all_magic')
126 | print('Your global config has been updated')
127 | else:
128 | print('Your global config has not been changed, however',
129 | args.package,
130 | 'is not configured to work on your system')
131 |
132 | def rerun(args):
133 | '''
134 | Re evaulate all cells in notebook
135 | '''
136 | parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
137 | parser.prog += ' ' + sys.argv[1]
138 | parser.add_argument('input_dir',
139 | type=str,
140 | default='nbplain',
141 | nargs='?',
142 | help='Plain notebook directory')
143 | parser.add_argument('--output_dir',
144 | type=str,
145 | default=None,
146 | help='Name of directory for re-evaluated notebooks')
147 | parser.add_argument('--clear_only',
148 | action='store_true',
149 | help='Clear the cells, but do not re-evaluate')
150 | parser.add_argument('--allow_errors',
151 | action='store_true',
152 | help='Continue running notebook even if errors occur')
153 | parser.add_argument('--timeout',
154 | type=int,
155 | default=60,
156 | help='Number of seconds to allow each cell to run for')
157 | args, unknown = parser.parse_known_args(sys.argv[2:])
158 | # Make output directory same as input unless otherwise specified
159 | if args.output_dir is None:
160 | args.output_dir = args.input_dir
161 |
162 | # Get directory contents
163 | if os.path.isdir(args.input_dir):
164 | contents, solution_contents = nbftools.directory_contents(args.input_dir)
165 | contents += solution_contents
166 |
167 | # Create output directory
168 | if not os.path.isdir(args.output_dir):
169 | try:
170 | os.mkdir(args.output_dir)
171 | except FileExistsError:
172 | print('Directory', args.output_dir, 'already exists')
173 | else:
174 | raise FileNotFoundError(2, 'No such directory', args.input_dir)
175 |
176 | # Create preprocessors
177 | clear_pre = nc.preprocessors.ClearOutputPreprocessor()
178 | exec_pre = nc.preprocessors.ExecutePreprocessor(timeout=args.timeout,
179 | allow_errors=args.allow_errors)
180 | if args.allow_errors:
181 | print('Warning: Notebooks are being run with --allow_errors flag')
182 | print('\tYou will not be notified of any errors and it is your')
183 | print('\tresponsibility to verify the output is correct.')
184 |
185 | # Loop over contents of directory
186 | for infile in contents:
187 | # Read in notebook
188 | print('Reading input file:', infile)
189 | notebook = nf.read(os.path.join(args.input_dir, infile), nf.NO_CONVERT)
190 |
191 | # Clear or clear and reexecute
192 | if args.clear_only:
193 | clear_pre.preprocess(notebook, {'metadata': {'path': args.output_dir}})
194 | else:
195 | try:
196 | # Needs to be output dir NOT input dir
197 | exec_pre.preprocess(notebook, {'metadata': {'path': args.output_dir}})
198 | except nc.preprocessors.CellExecutionError as e:
199 | print('Error: While executing the notebook', infile)
200 | print(e)
201 | print('Warning: notebook will be written, but some cells may not have executed')
202 | print('\tIf you want to continue running beyond this error try the --allow_errors flag')
203 |
204 | # Write out notebook
205 | print('Writing output file:', infile)
206 | nf.write(notebook, os.path.join(args.output_dir, infile))
207 |
208 | def render(args):
209 | '''
210 | Render plain notebooks as fancy notebooks
211 | '''
212 | from urllib.parse import quote as urlquote
213 |
214 | parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
215 | parser.prog += ' ' + sys.argv[1]
216 | parser.add_argument('input_dir',
217 | type=str,
218 | default='nbplain',
219 | nargs='?',
220 | help='Plain notebook directory')
221 | parser.add_argument('--output_dir',
222 | type=str,
223 | default='nbfancy',
224 | help='Directory to output rendered notebooks to')
225 | parser.add_argument('-c', '--config',
226 | type=str,
227 | default='config',
228 | help='Custom configuration directory')
229 | args, unknown = parser.parse_known_args(sys.argv[2:])
230 |
231 | if os.path.isdir(args.input_dir):
232 | contents, solution_contents = nbftools.directory_contents(args.input_dir)
233 |
234 | # Use config if specified, both fallback to global if error
235 | if args.config:
236 | configdir = args.config
237 | else:
238 | configdir = os.path.join(args.inputdir, 'config')
239 |
240 | # Read in the header, box config, box template and footer file
241 | header = nbftools.read_header(configdir)
242 | template = nbftools.read_box_template(configdir)
243 | config = nbftools.read_box_colour_config(configdir)
244 | footer = nbftools.read_footer(configdir)
245 |
246 | # Create output directory
247 | if not os.path.isdir(args.output_dir):
248 | try:
249 | os.mkdir(args.output_dir)
250 | except FileExistsError:
251 | print('Directory', args.output_dir, 'already exists')
252 | else:
253 | raise FileNotFoundError(2, 'No such directory', args.input_dir)
254 |
255 | # Loop over contents of directory (excluding solution files)
256 | for infile in contents:
257 | # Read input file
258 | print('Reading input file:', infile)
259 | plain = nf.read(os.path.join(args.input_dir, infile), nf.NO_CONVERT)
260 | solnfilename = infile.replace('.ipynb', '-soln.ipynb')
261 |
262 | # Render
263 | rendered, soln = nbftools.notebook2rendered(plain,
264 | config,
265 | template,
266 | solnfilename)
267 |
268 | # Add header
269 | rendered['cells'].insert(0, header)
270 |
271 | # Add navigation to footer
272 | triple = {'index' : './00_schedule.ipynb'} # Prevent error
273 | triple = nbftools.navigation_triple(args.input_dir, infile)
274 |
275 | tmp_footer = footer.copy()
276 | tmp_footer['source'] = footer['source'].format_map(triple)
277 | if triple['index'] != ('./' + infile):
278 | rendered['cells'].append(tmp_footer)
279 |
280 | # Remove cell toolbars and add scroll to slides
281 | rendered['metadata']['celltoolbar'] = 'None'
282 | rendered['metadata']['livereveal'] = {'scroll' : True}
283 |
284 | # Write the new notebook
285 | print('Writing output file:', infile)
286 | nf.write(plain, os.path.join(args.output_dir, infile))
287 |
288 | # If needed also write the solutions notebook
289 | if soln is not None:
290 | # Add header
291 | soln['cells'].insert(0, header)
292 | soln['metadata']['celltoolbar'] = 'None'
293 |
294 | print('Writing output file:', solnfilename)
295 | nf.write(soln, os.path.join(args.output_dir, solnfilename))
296 |
297 |
298 | def html(args):
299 | '''
300 | Publish fancy (or even plain) notebooks as html
301 | '''
302 | parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
303 | parser.prog += ' ' + sys.argv[1]
304 | parser.add_argument('input_dir',
305 | type=str,
306 | default='nbfancy',
307 | nargs='?',
308 | help='Fancy notebook directory')
309 | parser.add_argument('--output_dir',
310 | type=str,
311 | default='html',
312 | help='Directory to output html pages to')
313 | parser.add_argument('--include_slides',
314 | action='store_true',
315 | help='Includes html slides in slides directory inside output_dir')
316 | parser.add_argument('--redirect',
317 | type=str,
318 | default='00_schedule.html',
319 | help='page to automatically redirect to')
320 | args, unknown = parser.parse_known_args(sys.argv[2:])
321 |
322 | if not os.path.isdir(args.output_dir):
323 | try:
324 | os.mkdir(args.output_dir)
325 | except FileExistsError:
326 | print('Directory', args.output_dir, 'already exists')
327 |
328 | # Collect all input files
329 | if os.path.isdir(args.input_dir):
330 | contents, solution_contents = nbftools.directory_contents(args.input_dir)
331 | contents += solution_contents
332 | else:
333 | raise FileNotFoundError(2, 'No such directory', args.input_dir)
334 |
335 | # Create output directory if not already present
336 | cwd = os.getcwd()
337 | make_dir = os.path.join(cwd, args.output_dir)
338 | if not os.path.isdir(make_dir):
339 | try:
340 | os.mkdir(make_dir)
341 | except FileExistsError:
342 | print('Directory', make_dir, 'already exists')
343 |
344 | # Copy all resources to output directory
345 | resource_package = 'nbfancy'
346 | config_path = '/tools/css' # Do not use os.path.join()
347 | css_dir = pkg_resources.resource_filename(resource_package, config_path)
348 |
349 | # Copy our custom CSS directory to output directory
350 | copy_tree(css_dir, args.output_dir)
351 |
352 | # Copy local resource directories
353 | dir_list = ['images', 'code', 'data']
354 | for idir in dir_list:
355 | try:
356 | copy_tree(idir, os.path.join(args.output_dir, idir))
357 | except:
358 | print(idir, 'directory not found, not copying')
359 |
360 | # Make a slides directory
361 | slides_dir = os.path.join(args.output_dir, 'slides')
362 | if args.include_slides and not os.path.isdir(slides_dir):
363 | try:
364 | os.mkdir(slides_dir)
365 | except FileExistsError:
366 | print('Directory', slides_dir, 'already exists')
367 |
368 | # Convert all collected input files (makes slides too)
369 | for infile in contents:
370 | # Read input file
371 | print('Reading input file:', infile)
372 | html = nbftools.notebook2HTML(os.path.join(args.input_dir, infile))
373 | if args.include_slides:
374 | slides = nbftools.notebook2slides(os.path.join(args.input_dir, infile))
375 |
376 | # Name the output file
377 | outfilename = infile.replace('.ipynb', '.html')
378 | outpath = os.path.join(args.output_dir, outfilename)
379 |
380 | # Write webpage
381 | print('Writing output file:', outfilename)
382 | with open(outpath, 'w') as fh:
383 | fh.write(html)
384 |
385 | # Write slideshow
386 | if args.include_slides:
387 | slides_path = os.path.join(slides_dir, outfilename)
388 | print('Writing slides output file:', outfilename)
389 | with open(slides_path, 'w') as fh:
390 | fh.write(slides)
391 |
392 | # Add a redirect
393 | redirect_path = '/tools' # Do not use os.path.join()
394 | redirect_dir = pkg_resources.resource_filename(resource_package, redirect_path)
395 | print('Reading redirect template')
396 | with open(os.path.join(redirect_dir, 'redirect.html'), 'r') as fh:
397 | redirect_template = fh.read()
398 |
399 | redirect_html = redirect_template.format_map({'page' : args.redirect})
400 | print('Reading redirect file')
401 | with open(os.path.join(args.output_dir, 'index.html'), 'w') as fh:
402 | fh.write(redirect_html)
403 |
404 | def main():
405 | '''
406 | Checks for the verb used with nbfancy command
407 | '''
408 | # Parse all of the command line options
409 | parser = argparse.ArgumentParser()
410 | parser.add_argument('verb',
411 | choices=['init', 'hello', 'configure', 'rerun', 'render', 'html'],
412 | help='action to perform. Try adding --help to one of these options for more usage information')
413 |
414 | # Check that an argument was passed, if not print a more helpful message
415 | if len(sys.argv)==1:
416 | parser.print_help(sys.stderr)
417 | sys.exit(1)
418 | args = parser.parse_args(sys.argv[1:2])
419 |
420 | # Call function with given name
421 |
422 | call = globals()[args.verb]
423 | call(args)
424 |
425 | if __name__ == '__main__':
426 | main()
427 |
--------------------------------------------------------------------------------
/nbfancy/tools/css/css/w3.css:
--------------------------------------------------------------------------------
1 | /* W3.CSS 4.10 February 2018 by Jan Egil and Borge Refsnes */
2 | html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
3 | /* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
4 | html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
5 | article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}
6 | audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
7 | audio:not([controls]){display:none;height:0}[hidden],template{display:none}
8 | a{background-color:transparent;-webkit-text-decoration-skip:objects}
9 | a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
10 | dfn{font-style:italic}mark{background:#ff0;color:#000}
11 | small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
12 | sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}svg:not(:root){overflow:hidden}
13 | code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
14 | button,input,select,textarea{font:inherit;margin:0}optgroup{font-weight:bold}
15 | button,input{overflow:visible}button,select{text-transform:none}
16 | button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}
17 | button::-moz-focus-inner, [type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner{border-style:none;padding:0}
18 | button:-moz-focusring, [type=button]:-moz-focusring, [type=reset]:-moz-focusring, [type=submit]:-moz-focusring{outline:1px dotted ButtonText}
19 | fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
20 | legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
21 | [type=checkbox],[type=radio]{padding:0}
22 | [type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
23 | [type=search]{-webkit-appearance:textfield;outline-offset:-2px}
24 | [type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}
25 | ::-webkit-input-placeholder{color:inherit;opacity:0.54}
26 | ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
27 | /* End extract */
28 | html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
29 | h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}.w3-serif{font-family:serif}
30 | h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
31 | hr{border:0;border-top:1px solid #eee;margin:20px 0}
32 | .w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
33 | .w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
34 | .w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
35 | .w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
36 | .w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
37 | .w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
38 | .w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
39 | .w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
40 | .w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
41 | .w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
42 | .w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
43 | .w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
44 | .w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
45 | .w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
46 | .w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
47 | .w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
48 | .w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
49 | .w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
50 | .w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
51 | .w3-dropdown-hover:hover .w3-dropdown-content{display:block}
52 | .w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
53 | .w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
54 | .w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
55 | .w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
56 | .w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
57 | .w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
58 | .w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
59 | .w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
60 | .w3-main,#main{transition:margin-left .4s}
61 | .w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
62 | .w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
63 | .w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
64 | .w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
65 | .w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
66 | .w3-bar .w3-button{white-space:normal}
67 | .w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
68 | .w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
69 | .w3-responsive{display:block;overflow-x:auto}
70 | .w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
71 | .w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
72 | .w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
73 | .w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
74 | .w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
75 | .w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
76 | @media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
77 | .w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
78 | .w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
79 | @media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
80 | .w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
81 | .w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
82 | .w3-content{max-width:980px;margin:auto}.w3-rest{overflow:hidden}
83 | .w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
84 | .w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
85 | .w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
86 | @media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
87 | .w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
88 | .w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
89 | .w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
90 | @media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
91 | @media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
92 | @media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
93 | @media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}}
94 | .w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
95 | .w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
96 | .w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
97 | .w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
98 | .w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
99 | .w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
100 | .w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
101 | .w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
102 | .w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
103 | .w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
104 | .w3-display-position{position:absolute}
105 | .w3-circle{border-radius:50%}
106 | .w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
107 | .w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
108 | .w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
109 | .w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
110 | .w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
111 | .w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
112 | .w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
113 | .w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
114 | .w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
115 | .w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
116 | .w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
117 | .w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
118 | .w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
119 | .w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
120 | .w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
121 | .w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
122 | .w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
123 | .w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
124 | .w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
125 | .w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
126 | .w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
127 | .w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
128 | .w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
129 | .w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
130 | .w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
131 | .w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
132 | .w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
133 | .w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
134 | .w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
135 | .w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
136 | .w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
137 | .w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
138 | .w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
139 | .w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
140 | .w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
141 | .w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
142 | .w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
143 | .w3-left{float:left!important}.w3-right{float:right!important}
144 | .w3-button:hover{color:#000!important;background-color:#ccc!important}
145 | .w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
146 | .w3-hover-none:hover{box-shadow:none!important}
147 | /* Colors */
148 | .w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
149 | .w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
150 | .w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
151 | .w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
152 | .w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
153 | .w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
154 | .w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
155 | .w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
156 | .w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
157 | .w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
158 | .w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
159 | .w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
160 | .w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
161 | .w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
162 | .w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
163 | .w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
164 | .w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
165 | .w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
166 | .w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
167 | .w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
168 | .w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
169 | .w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
170 | .w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
171 | .w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
172 | .w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
173 | .w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
174 | .w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
175 | .w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
176 | .w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
177 | .w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
178 | .w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
179 | .w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
180 | .w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
181 | .w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
182 | .w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
183 | .w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
184 | .w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
185 | .w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
186 | .w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
187 | .w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
188 | .w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
189 | .w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
190 | .w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
191 | .w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
192 | .w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
193 | .w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
194 | .w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
195 | .w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
196 | .w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
197 | .w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
198 | .w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
199 | .w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
200 | .w3-text-black,.w3-hover-text-black:hover{color:#000!important}
201 | .w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
202 | .w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
203 | .w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
204 | .w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
205 | .w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
206 | .w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
207 | .w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
208 | .w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
209 | .w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
210 | .w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
211 | .w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
212 | .w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
213 | .w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
214 | .w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
215 | .w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
216 | .w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
217 | .w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
218 | .w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
219 | .w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
220 | .w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
221 | .w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
222 | .w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
223 | .w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
224 | .w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
225 | .w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
226 | .w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
227 | .w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
228 | .w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
229 | .w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
230 | .w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
231 | .w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
--------------------------------------------------------------------------------
/nbfancy/tutorial/05_environments.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "slideshow": {
7 | "slide_type": "slide"
8 | }
9 | },
10 | "source": [
11 | "# Environments"
12 | ]
13 | },
14 | {
15 | "cell_type": "markdown",
16 | "metadata": {
17 | "slideshow": {
18 | "slide_type": "subslide"
19 | }
20 | },
21 | "source": [
22 | "## Overview:\n",
23 | "- **Teaching:** 10 min\n",
24 | "- **Exercises:** 0 min\n",
25 | "\n",
26 | "**Questions**\n",
27 | "- What decoration does NBfancy provide?\n",
28 | "- How do I use NBFancy to decorate my notebooks?\n",
29 | "\n",
30 | "**Objectives**\n",
31 | "- Learn what NBfancy environments do and how to use them"
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {
37 | "slideshow": {
38 | "slide_type": "slide"
39 | }
40 | },
41 | "source": [
42 | "## General\n",
43 | "Keywords can be entered at the start of any markdown cell in a notebook to indicate that it requires additional markup. We call these specailly marked up cells environments. The general syntax is `## Keyword: Optional title`, where:\n",
44 | "\n",
45 | "* There can be any number of hashes at the start\n",
46 | "* The keyword that defines the environment\n",
47 | "* A colon is **required**, even if no optional title is provided\n",
48 | "* An optional title can be given, which will appear in the cell\n",
49 | "\n",
50 | "This structure for the keywords was chosen so that it still displays well in an unrendered notebook, ie: when you are creating the material. The specific format has been chosen for ease of use and to mitigate against accidental decoration.\n",
51 | "\n",
52 | "*All headings get normalised to `##` regardless of how many hashes are used*"
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {
58 | "slideshow": {
59 | "slide_type": "slide"
60 | }
61 | },
62 | "source": [
63 | "The keywords available by default, in the order they will normally be encountered, are\n",
64 | "* `Prerequisites`\n",
65 | "* `Schedule`\n",
66 | "* `Setup`\n",
67 | "These are ususally reserved for the `00_schedule.ipynb` page, but can be used elsewhere if required and\n",
68 | "* `Overview`\n",
69 | "* `Information`\n",
70 | "* `Exercise`\n",
71 | "* `Solution`\n",
72 | "* `Key Points`\n",
73 | "These are normally found in each `XX_episode.ipynb` page.\n",
74 | "\n",
75 | "Below we outline how each environment works. In the sections on [keywords](08_keywords.ipynb) and [template](09_template.ipynb), we outline how we can change some of the default behaviour."
76 | ]
77 | },
78 | {
79 | "cell_type": "markdown",
80 | "metadata": {
81 | "slideshow": {
82 | "slide_type": "slide"
83 | }
84 | },
85 | "source": [
86 | "### Format\n",
87 | "We give a description of each environment.\n",
88 | "\n",
89 | "This will be followed by three 'cells':\n",
90 | "1. The first uses `%%markdown` magic to show what the plain text you enter looks like. \n",
91 | "2. The output of this cell, the second 'cell' is how the environment will render in the plain notebook\n",
92 | "3. The third cell is what the cell will look like once `nbfancy render` has been used.\n",
93 | "\n",
94 | "You should just enter the plain text **after** `%%markdown` into a markdown cell in a notebook and once rendered it will appear as the third example."
95 | ]
96 | },
97 | {
98 | "cell_type": "markdown",
99 | "metadata": {
100 | "slideshow": {
101 | "slide_type": "slide"
102 | }
103 | },
104 | "source": [
105 | "## Prerequisites\n",
106 | "\n",
107 | "By default the **prerequisites** environment is coloured green, is higlighted with a star symbol and the keyword is kept in the title."
108 | ]
109 | },
110 | {
111 | "cell_type": "code",
112 | "execution_count": 1,
113 | "metadata": {
114 | "slideshow": {
115 | "slide_type": "subslide"
116 | }
117 | },
118 | "outputs": [
119 | {
120 | "data": {
121 | "text/markdown": [
122 | "## Prerequisites:\n",
123 | "* Bulleted\n",
124 | "* List\n",
125 | "* of\n",
126 | "* items\n"
127 | ],
128 | "text/plain": [
129 | ""
130 | ]
131 | },
132 | "metadata": {},
133 | "output_type": "display_data"
134 | }
135 | ],
136 | "source": [
137 | "%%markdown\n",
138 | "## Prerequisites:\n",
139 | "* Bulleted\n",
140 | "* List\n",
141 | "* of\n",
142 | "* items"
143 | ]
144 | },
145 | {
146 | "cell_type": "markdown",
147 | "metadata": {
148 | "slideshow": {
149 | "slide_type": "subslide"
150 | }
151 | },
152 | "source": [
153 | "## Prerequisites:\n",
154 | "* Bulleted\n",
155 | "* List\n",
156 | "* of\n",
157 | "* items"
158 | ]
159 | },
160 | {
161 | "cell_type": "markdown",
162 | "metadata": {
163 | "slideshow": {
164 | "slide_type": "slide"
165 | }
166 | },
167 | "source": [
168 | "## Schedule\n",
169 | "The **schedule** environment is white as most of the body will usually be filled with a table of links to the various parts of the lesson. It uses the clock symbol and by default the keyword is kept."
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": 4,
175 | "metadata": {
176 | "slideshow": {
177 | "slide_type": "subslide"
178 | }
179 | },
180 | "outputs": [
181 | {
182 | "data": {
183 | "text/markdown": [
184 | "## Schedule:\n",
185 | "| Time | Episode | Description |\n",
186 | "|---|---|---|\n",
187 | "| 0:00 | [Introdction](01_intro.ipynb) | A good start |\n",
188 | "| 0:10 | [Part One](02_part_one.ipynb) | The first part |\n",
189 | "| 0:20 | [Part Two](03_part_two.ipynb) | The next part |\n"
190 | ],
191 | "text/plain": [
192 | ""
193 | ]
194 | },
195 | "metadata": {},
196 | "output_type": "display_data"
197 | }
198 | ],
199 | "source": [
200 | "%%markdown\n",
201 | "## Schedule:\n",
202 | "| Time | Episode | Description |\n",
203 | "|---|---|---|\n",
204 | "| 0:00 | [Introdction](01_intro.ipynb) | A good start |\n",
205 | "| 0:10 | [Part One](02_part_one.ipynb) | The first part |\n",
206 | "| 0:20 | [Part Two](03_part_two.ipynb) | The next part |"
207 | ]
208 | },
209 | {
210 | "cell_type": "markdown",
211 | "metadata": {
212 | "slideshow": {
213 | "slide_type": "subslide"
214 | }
215 | },
216 | "source": [
217 | "## Schedule:\n",
218 | "| Time | Episode | Description |\n",
219 | "|---|---|---|\n",
220 | "| 0:00 | [Introdction](01_intro.ipynb) | A good start |\n",
221 | "| 0:10 | [Part One](02_part_one.ipynb) | The first part |\n",
222 | "| 0:20 | [Part Two](03_part_two.ipynb) | The next part |"
223 | ]
224 | },
225 | {
226 | "cell_type": "markdown",
227 | "metadata": {
228 | "slideshow": {
229 | "slide_type": "slide"
230 | }
231 | },
232 | "source": [
233 | "## Setup\n",
234 | "The **setup** environment is also green, in general the first and last environment you see on each page will be green, but none on the intermediate environments will be. The symbol is gears and the keyword is retained.\n",
235 | "\n",
236 | "When the setup is lengthy we have alternatively included a specific notebook `00_setup.ipynb` and an additional link in the schedule."
237 | ]
238 | },
239 | {
240 | "cell_type": "code",
241 | "execution_count": 5,
242 | "metadata": {
243 | "slideshow": {
244 | "slide_type": "subslide"
245 | }
246 | },
247 | "outputs": [
248 | {
249 | "data": {
250 | "text/markdown": [
251 | "## Setup:\n",
252 | "Install NBfancy by executing `pip install nbfancy`\n"
253 | ],
254 | "text/plain": [
255 | ""
256 | ]
257 | },
258 | "metadata": {},
259 | "output_type": "display_data"
260 | }
261 | ],
262 | "source": [
263 | "%%markdown\n",
264 | "## Setup:\n",
265 | "Install NBfancy by executing `pip install nbfancy`"
266 | ]
267 | },
268 | {
269 | "cell_type": "markdown",
270 | "metadata": {
271 | "slideshow": {
272 | "slide_type": "subslide"
273 | }
274 | },
275 | "source": [
276 | "## Setup:\n",
277 | "Install NBfancy by executing `pip install nbfancy`"
278 | ]
279 | },
280 | {
281 | "cell_type": "markdown",
282 | "metadata": {
283 | "slideshow": {
284 | "slide_type": "slide"
285 | }
286 | },
287 | "source": [
288 | "## Overview\n",
289 | "The **overview** environment appears at the start of each section or episode and hence is coloured green. It has uses the page icon and retains the keyword. The overview environment is useful for outlining the timings for a section, the key questions that will be answered and the learning objectives. We suggest the following layout:"
290 | ]
291 | },
292 | {
293 | "cell_type": "code",
294 | "execution_count": 1,
295 | "metadata": {
296 | "slideshow": {
297 | "slide_type": "subslide"
298 | }
299 | },
300 | "outputs": [
301 | {
302 | "data": {
303 | "text/markdown": [
304 | "## Overview:\n",
305 | "- **Teaching:** 10 min\n",
306 | "- **Exercises:** 10 min\n",
307 | "\n",
308 | "**Questions**\n",
309 | "- Is this a list of questions?\n",
310 | "\n",
311 | "**Objectives**\n",
312 | "- This is a list of outcomes\n"
313 | ],
314 | "text/plain": [
315 | ""
316 | ]
317 | },
318 | "metadata": {},
319 | "output_type": "display_data"
320 | }
321 | ],
322 | "source": [
323 | "%%markdown\n",
324 | "## Overview:\n",
325 | "- **Teaching:** 10 min\n",
326 | "- **Exercises:** 10 min\n",
327 | "\n",
328 | "**Questions**\n",
329 | "- Is this a list of questions?\n",
330 | "\n",
331 | "**Objectives**\n",
332 | "- This is a list of outcomes"
333 | ]
334 | },
335 | {
336 | "cell_type": "markdown",
337 | "metadata": {
338 | "slideshow": {
339 | "slide_type": "subslide"
340 | }
341 | },
342 | "source": [
343 | "## Overview:\n",
344 | "- **Teaching:** 10 min\n",
345 | "- **Exercises:** 10 min\n",
346 | "\n",
347 | "**Questions**\n",
348 | "- Is this a list of questions?\n",
349 | "\n",
350 | "**Objectives**\n",
351 | "- This is a list of outcomes"
352 | ]
353 | },
354 | {
355 | "cell_type": "markdown",
356 | "metadata": {
357 | "slideshow": {
358 | "slide_type": "slide"
359 | }
360 | },
361 | "source": [
362 | "## Information\n",
363 | "The **information** environment is coloured blue and can be used anywhere in the body of a lesson where special attention is required or additional information is available. It uses the information circle symbol and the keyword is removed on use. This means that the optional title will be used as the title, and if no optional title is provided, only the symbol will appear."
364 | ]
365 | },
366 | {
367 | "cell_type": "code",
368 | "execution_count": 7,
369 | "metadata": {
370 | "slideshow": {
371 | "slide_type": "subslide"
372 | }
373 | },
374 | "outputs": [
375 | {
376 | "data": {
377 | "text/markdown": [
378 | "## Information: A useful tip\n",
379 | "Here is some additional information about that thing we just did\n"
380 | ],
381 | "text/plain": [
382 | ""
383 | ]
384 | },
385 | "metadata": {},
386 | "output_type": "display_data"
387 | }
388 | ],
389 | "source": [
390 | "%%markdown\n",
391 | "## Information: A useful tip\n",
392 | "Here is some additional information about that thing we just did"
393 | ]
394 | },
395 | {
396 | "cell_type": "markdown",
397 | "metadata": {
398 | "slideshow": {
399 | "slide_type": "subslide"
400 | }
401 | },
402 | "source": [
403 | "## Information: A useful tip\n",
404 | "Here is some additional information about that thing we just did"
405 | ]
406 | },
407 | {
408 | "cell_type": "markdown",
409 | "metadata": {
410 | "slideshow": {
411 | "slide_type": "slide"
412 | }
413 | },
414 | "source": [
415 | "## Exercise and Solution environments\n",
416 | "The **exercise** environment is yellow and has a pencil and pad as a symbol. The keyword is removed on use, but you can use the optional title to give exercises titles with a number at the start if desired. There are number of ways of using the exercise environment and its optional partner, solution. \n",
417 | "\n",
418 | "The **solution** environment is blue and uses the eye symbol. As with the exercise environment the keyword is removed, and a number can be used at the start. You won't see any solution environments on this page! So as not to give the answers immediately after the exercise the solutions are placed in a seperate notebook. This allows you to create an exercise and solution together, but these get seperated by the NBfancy tool. The solution link defined in the exercise will point to the solutions page and even scroll the page to the relevant solution, if it were to appear off screen.\n",
419 | "\n",
420 | "We will demonstrate four different ways of using the exercise/solution environments."
421 | ]
422 | },
423 | {
424 | "cell_type": "markdown",
425 | "metadata": {
426 | "slideshow": {
427 | "slide_type": "slide"
428 | }
429 | },
430 | "source": [
431 | "### Exercise only\n",
432 | "First an exercise with no solution, for when no solutions is provided/required."
433 | ]
434 | },
435 | {
436 | "cell_type": "code",
437 | "execution_count": 9,
438 | "metadata": {
439 | "slideshow": {
440 | "slide_type": "subslide"
441 | }
442 | },
443 | "outputs": [
444 | {
445 | "data": {
446 | "text/markdown": [
447 | "## Exercise: 1\n",
448 | "Do the first task\n"
449 | ],
450 | "text/plain": [
451 | ""
452 | ]
453 | },
454 | "metadata": {},
455 | "output_type": "display_data"
456 | }
457 | ],
458 | "source": [
459 | "%%markdown\n",
460 | "## Exercise: 1\n",
461 | "Do the first task"
462 | ]
463 | },
464 | {
465 | "cell_type": "markdown",
466 | "metadata": {
467 | "slideshow": {
468 | "slide_type": "subslide"
469 | }
470 | },
471 | "source": [
472 | "## Exercise: 1\n",
473 | "Do the first task"
474 | ]
475 | },
476 | {
477 | "cell_type": "markdown",
478 | "metadata": {
479 | "slideshow": {
480 | "slide_type": "slide"
481 | }
482 | },
483 | "source": [
484 | "### Exercise and separate solution\n",
485 | "Second an exercise with no inline solution, but which creates a link to a solution notebook. This is useful if you have already defined solutions in a separate notebook. Currently, the notebooks must have the same root name but with the solution ending with `-soln`. That is if your notebook is called `01_lesson.ipynb` then the link will point to `01_lesson-soln.ipynb`.\n",
486 | "\n",
487 | "If the exercise environment ends with an empty markdown link: `[Solution]()` then a link will be created to the solution **automatically**, if it has the same title."
488 | ]
489 | },
490 | {
491 | "cell_type": "code",
492 | "execution_count": 10,
493 | "metadata": {
494 | "slideshow": {
495 | "slide_type": "subslide"
496 | }
497 | },
498 | "outputs": [
499 | {
500 | "data": {
501 | "text/markdown": [
502 | "## Exercise: 2\n",
503 | "Do the second task\n",
504 | "\n",
505 | "[Solution]()\n"
506 | ],
507 | "text/plain": [
508 | ""
509 | ]
510 | },
511 | "metadata": {},
512 | "output_type": "display_data"
513 | }
514 | ],
515 | "source": [
516 | "%%markdown\n",
517 | "## Exercise: 2\n",
518 | "Do the second task\n",
519 | "\n",
520 | "[Solution]()"
521 | ]
522 | },
523 | {
524 | "cell_type": "markdown",
525 | "metadata": {
526 | "slideshow": {
527 | "slide_type": "subslide"
528 | }
529 | },
530 | "source": [
531 | "## Exercise: 2\n",
532 | "Do the second task\n",
533 | "\n",
534 | "[Solution]()"
535 | ]
536 | },
537 | {
538 | "cell_type": "markdown",
539 | "metadata": {
540 | "slideshow": {
541 | "slide_type": "slide"
542 | }
543 | },
544 | "source": [
545 | "### Exercise and solution, missing link\n",
546 | "\n",
547 | "Third an exercise with a solution defined after it, but no link included in the exercise. This is usually an error, but included to demonstrate that the solution is not lost, just difficult to get to!"
548 | ]
549 | },
550 | {
551 | "cell_type": "code",
552 | "execution_count": 11,
553 | "metadata": {
554 | "slideshow": {
555 | "slide_type": "subslide"
556 | }
557 | },
558 | "outputs": [
559 | {
560 | "data": {
561 | "text/markdown": [
562 | "## Exercise: 3\n",
563 | "Do the third task\n"
564 | ],
565 | "text/plain": [
566 | ""
567 | ]
568 | },
569 | "metadata": {},
570 | "output_type": "display_data"
571 | }
572 | ],
573 | "source": [
574 | "%%markdown\n",
575 | "## Exercise: 3\n",
576 | "Do the third task"
577 | ]
578 | },
579 | {
580 | "cell_type": "markdown",
581 | "metadata": {
582 | "slideshow": {
583 | "slide_type": "subslide"
584 | }
585 | },
586 | "source": [
587 | "## Exercise: 3\n",
588 | "Do the third task"
589 | ]
590 | },
591 | {
592 | "cell_type": "code",
593 | "execution_count": 14,
594 | "metadata": {
595 | "slideshow": {
596 | "slide_type": "subslide"
597 | }
598 | },
599 | "outputs": [
600 | {
601 | "data": {
602 | "text/markdown": [
603 | "## Solution: 3\n",
604 | "Everyone gets stuck on number 3\n",
605 | "\n",
606 | "BIG BLANK SPACE REQUIRED\n"
607 | ],
608 | "text/plain": [
609 | ""
610 | ]
611 | },
612 | "metadata": {},
613 | "output_type": "display_data"
614 | }
615 | ],
616 | "source": [
617 | "%%markdown\n",
618 | "## Solution: 3\n",
619 | "Everyone gets stuck on number 3\n",
620 | "\n",
621 | "BIG BLANK SPACE REQUIRED"
622 | ]
623 | },
624 | {
625 | "cell_type": "markdown",
626 | "metadata": {
627 | "slideshow": {
628 | "slide_type": "subslide"
629 | }
630 | },
631 | "source": [
632 | "## Solution: 3\n",
633 | "Everyone gets stuck on number 3\n",
634 | "\n",
635 | "BIG BLANK SPACE REQUIRED"
636 | ]
637 | },
638 | {
639 | "cell_type": "markdown",
640 | "metadata": {
641 | "slideshow": {
642 | "slide_type": "slide"
643 | }
644 | },
645 | "source": [
646 | "### Exercise and Solution\n",
647 | "\n",
648 | "Fourth and finally an exercise with a solution defined after it, with an empty link included in the exercise. This is the intended typical use of the exercise and solution pair."
649 | ]
650 | },
651 | {
652 | "cell_type": "code",
653 | "execution_count": 15,
654 | "metadata": {
655 | "slideshow": {
656 | "slide_type": "subslide"
657 | }
658 | },
659 | "outputs": [
660 | {
661 | "data": {
662 | "text/markdown": [
663 | "## Exercise: 4\n",
664 | "Do the fourth task\n",
665 | "\n",
666 | "[Solution]()\n"
667 | ],
668 | "text/plain": [
669 | ""
670 | ]
671 | },
672 | "metadata": {},
673 | "output_type": "display_data"
674 | }
675 | ],
676 | "source": [
677 | "%%markdown\n",
678 | "## Exercise: 4\n",
679 | "Do the fourth task\n",
680 | "\n",
681 | "[Solution]()"
682 | ]
683 | },
684 | {
685 | "cell_type": "markdown",
686 | "metadata": {
687 | "slideshow": {
688 | "slide_type": "subslide"
689 | }
690 | },
691 | "source": [
692 | "## Exercise: 4\n",
693 | "Do the fourth task\n",
694 | "\n",
695 | "[Solution]()"
696 | ]
697 | },
698 | {
699 | "cell_type": "code",
700 | "execution_count": 16,
701 | "metadata": {
702 | "slideshow": {
703 | "slide_type": "subslide"
704 | }
705 | },
706 | "outputs": [
707 | {
708 | "data": {
709 | "text/markdown": [
710 | "## Solution: 4\n",
711 | "The fourth task was trivial\n",
712 | "\n",
713 | "BIG BLANK LESS NECESSARY\n"
714 | ],
715 | "text/plain": [
716 | ""
717 | ]
718 | },
719 | "metadata": {},
720 | "output_type": "display_data"
721 | }
722 | ],
723 | "source": [
724 | "%%markdown\n",
725 | "## Solution: 4\n",
726 | "The fourth task was trivial\n",
727 | "\n",
728 | "BIG BLANK LESS NECESSARY"
729 | ]
730 | },
731 | {
732 | "cell_type": "markdown",
733 | "metadata": {
734 | "slideshow": {
735 | "slide_type": "subslide"
736 | }
737 | },
738 | "source": [
739 | "## Solution: 4\n",
740 | "The fourth task was trivial\n",
741 | "\n",
742 | "BIG BLANK LESS NECESSARY"
743 | ]
744 | },
745 | {
746 | "cell_type": "markdown",
747 | "metadata": {
748 | "slideshow": {
749 | "slide_type": "slide"
750 | }
751 | },
752 | "source": [
753 | "## Key Points\n",
754 | "The key points environment is typically used to close an episode and is coloured green. It uses the key as a symbol and the keyword is kept. By way of example, we will summarise the key points of this section here."
755 | ]
756 | },
757 | {
758 | "cell_type": "code",
759 | "execution_count": 8,
760 | "metadata": {
761 | "slideshow": {
762 | "slide_type": "subslide"
763 | }
764 | },
765 | "outputs": [
766 | {
767 | "data": {
768 | "text/markdown": [
769 | "## Key Points:\n",
770 | "- The general keyword syntax is `## Keyword: Optional title\n",
771 | "- `Prerequisites`, `Schedule` and `Setup` are generally used on the first page\n",
772 | "- Sections start with an `Overview` and finish with `Key Points`\n",
773 | "- `Information` highlights additional information in the lesson\n",
774 | "- `Exercise` and `Solution` can be used as a pair to set exercises\n"
775 | ],
776 | "text/plain": [
777 | ""
778 | ]
779 | },
780 | "metadata": {},
781 | "output_type": "display_data"
782 | }
783 | ],
784 | "source": [
785 | "%%markdown\n",
786 | "## Key Points:\n",
787 | "- The general keyword syntax is `## Keyword: Optional title`\n",
788 | "- `Prerequisites`, `Schedule` and `Setup` are generally used on the first page\n",
789 | "- Sections start with an `Overview` and finish with `Key Points`\n",
790 | "- `Information` highlights additional information in the lesson\n",
791 | "- `Exercise` and `Solution` can be used as a pair to set exercises"
792 | ]
793 | },
794 | {
795 | "cell_type": "markdown",
796 | "metadata": {
797 | "slideshow": {
798 | "slide_type": "subslide"
799 | }
800 | },
801 | "source": [
802 | "## Key Points:\n",
803 | "- The general keyword syntax is `## Keyword: Optional title`\n",
804 | "- `Prerequisites`, `Schedule` and `Setup` are generally used on the first page\n",
805 | "- Sections start with an `Overview` and finish with `Key Points`\n",
806 | "- `Information` highlights additional information in the lesson\n",
807 | "- `Exercise` and `Solution` can be used as a pair to set exercises"
808 | ]
809 | }
810 | ],
811 | "metadata": {
812 | "celltoolbar": "Slideshow",
813 | "kernelspec": {
814 | "display_name": "Python 3",
815 | "language": "python",
816 | "name": "python3"
817 | },
818 | "language_info": {
819 | "codemirror_mode": {
820 | "name": "ipython",
821 | "version": 3
822 | },
823 | "file_extension": ".py",
824 | "mimetype": "text/x-python",
825 | "name": "python",
826 | "nbconvert_exporter": "python",
827 | "pygments_lexer": "ipython3",
828 | "version": "3.7.3"
829 | }
830 | },
831 | "nbformat": 4,
832 | "nbformat_minor": 2
833 | }
834 |
--------------------------------------------------------------------------------