def remove_solutions(nb, nb_name): """Convert solution cells to markdown; embed images from Python output.""" # -- Extract image data from the cell outputs c = Config() template = (f"../static/{nb_name}" "_Solution_{cell_index}_{index}{extension}") c.ExtractOutputPreprocessor.output_filename_template = template # Note: using the RST exporter means we need to install pandoc as a dep # in the github workflow, which adds a little bit of latency, and we don't # actually care about the RST output. It's just a convenient way to get the # image resources the way we want them. exporter = RSTExporter() extractor = ExtractOutputPreprocessor(config=c) exporter.register_preprocessor(extractor, True) _, resources = exporter.from_notebook_node(nb) # -- Convert solution cells to markdown with embedded image nb_cells = nb.get("cells", []) outputs = resources["outputs"] solution_resources = {} for i, cell in enumerate(nb_cells): cell_text = cell["source"].replace(" ", "").lower() if cell_text.startswith("#@titlesolution"): # Just remove solution cells that generate no outputs if not cell["outputs"]: nb_cells.remove(cell) continue # Filter the resources for solution images image_paths = [k for k in outputs if f"Solution_{i}" in k] solution_resources.update({k: outputs[k] for k in image_paths}) # Conver the solution cell to markdown, strip the source, # and embed the image as a link to static resource new_source = "**Example output:**\n\n" + "\n\n".join( [f"<img src='{f}' align='left'>" for f in image_paths]) cell["source"] = new_source cell["cell_type"] = "markdown" del cell["outputs"] del cell["execution_count"] return nb, solution_resources
def compile_tutorial(tutorial_name, force_recompile=False): print('- Tutorial "' + tutorial_name + '"') notebook_path = 'tutorial_notebooks/' + tutorial_name + '/' + tutorial_name + '.ipynb' export_path = 'tutorials/' + tutorial_name + '/' + tutorial_name thumb_dest = os.path.dirname(export_path) + '/thumb.png' if not os.path.exists(os.path.dirname(export_path)): os.makedirs(os.path.dirname(export_path)) # Read in notebook print(' Reading notebook...') notebook = nbformat.read(notebook_path, 4) # Scrape title, description and thumbnail first_cell = notebook.cells[0] title = first_cell.source.splitlines()[0] if '#' in title: title = title.replace('#', '').strip() description = '' for line in first_cell.source.splitlines()[1:]: if line.strip(): description = line.strip() break if not description: print(' Description could not be found in the notebook.') if 'thumbnail_figure_index' in notebook.metadata: thumbnail_figure_index = notebook.metadata['thumbnail_figure_index'] else: thumbnail_figure_index = -1 if 'level' in notebook.metadata: level = notebook.metadata['level'].capitalize() elif 'difficulty' in notebook.metadata: level = notebook.metadata['difficulty'].capitalize() else: level = 'Unknown' # Check if the tutorial was already executed. if os.path.exists(export_path + '.rst'): if os.path.getmtime(export_path + '.rst') > os.path.getmtime(notebook_path): if force_recompile: print(' Already compiled. Recompiling anyway...') else: print(' Already compiled. Skipping compilation...') return title, level, description, thumb_dest.split('/', 1)[-1] # Execute notebook if not already executed already_executed = any( c.get('outputs') or c.get('execution_count') for c in notebook.cells if c.cell_type == 'code') resources = {'metadata': {'path': os.path.dirname(notebook_path)}} if not already_executed: print(' Executing', end='') start = time.time() additional_cell_1 = { "cell_type": "code", "execution_count": None, "metadata": {}, "outputs": [], "source": r"%matplotlib inline" + '\n' + r"%config InlineBackend.print_figure_kwargs = {'bbox_inches': None}" } additional_cell_2 = { "cell_type": "code", "execution_count": None, "metadata": {}, "outputs": [], "source": "import matplotlib as mpl\nmpl.rcParams['figure.figsize'] = (8, 6)\nmpl.rcParams['figure.dpi'] = 150\nmpl.rcParams['savefig.dpi'] = 150" } notebook.cells.insert(1, nbformat.from_dict(additional_cell_1)) notebook.cells.insert(2, nbformat.from_dict(additional_cell_2)) client = NotebookClient(nb=notebook, resources=resources, timeout=585, kernel_name='python3') try: with client.setup_kernel(): for i, cell in enumerate(notebook.cells): print('.', end='') client.execute_cell(cell, i) client.set_widgets_metadata() except CellExecutionError as err: print(' Error while processing notebook:') print(' ', err) print('') notebook.cells.pop(2) notebook.cells.pop(1) end = time.time() time_taken = end - start if time_taken > 60: print(' Execution took %dm%02ds.' % (time_taken / 60, time_taken % 60)) else: print(' Execution took %ds.' % time_taken) else: print(' Notebook was already executed.') print(' Rendering tutorial...') exporter = RSTExporter() output, resources = exporter.from_notebook_node(notebook, resources) writer = FilesWriter(build_directory=os.path.dirname(export_path)) writer.write(output, resources, notebook_name=os.path.basename(export_path)) pictures = sorted(resources['outputs'], key=output.find) try: thumbnail_source = pictures[thumbnail_figure_index] # Read in thumbnail source image img = Image.open(os.path.dirname(export_path) + '/' + thumbnail_source) # Trim whitespace bg = Image.new(img.mode, img.size, img.getpixel((0, 0))) diff = ImageChops.difference(img, bg) diff = ImageChops.add(diff, diff) bbox = diff.getbbox() if bbox: img = img.crop(bbox) # Resize image to have a width of 400px img.thumbnail([400, 1000]) # Save thumbnail img.save(thumb_dest) except: shutil.copyfile('_static/no_thumb.png', thumb_dest) print(' Done!') return title, level, description, thumb_dest.split('/', 1)[-1]
def compile_tutorial(tutorial_name, force_recompile=False): print('- Compiling tutorial ' + tutorial_name + '...') notebook_path = 'tutorial_notebooks/' + tutorial_name + '/' + tutorial_name + '.ipynb' export_path = 'tutorials/' + tutorial_name + '/' + tutorial_name thumb_dest = os.path.dirname(export_path) + '/thumb.png' if not os.path.exists(os.path.dirname(export_path)): os.makedirs(os.path.dirname(export_path)) # Read in notebook notebook = nbformat.read(notebook_path, 4) # Scrape title, description and thumbnail first_cell = notebook.cells[0] title = first_cell.source.splitlines()[0] if '#' in title: title = title.replace('#', '').strip() description = first_cell.source.splitlines()[2].strip() if 'thumbnail_figure_index' in notebook.metadata: thumbnail_figure_index = notebook.metadata['thumbnail_figure_index'] else: thumbnail_figure_index = -1 if 'level' in notebook.metadata: level = notebook.metadata['level'].capitalize() elif 'difficulty' in notebook.metadata: level = notebook.metadata['difficulty'].capitalize() else: level = 'Unknown' # Check if the tutorial was already compiled. if os.path.exists(export_path + '.rst'): if os.path.getmtime(export_path + '.rst') > os.path.getmtime(notebook_path): if force_recompile: print(' Already compiled. Recompiling anyway...') else: print(' Already compiled. Skipping...') return title, level, description, thumb_dest.split('/', 1)[-1] # Execute notebook if not already executed already_executed = any(c.get('outputs') or c.get('execution_count') for c in notebook.cells if c.cell_type == 'code') resources = {} if not already_executed: ep = ExecutePreprocessor(timeout=120, kernel_name='python3') try: notebook, resources = ep.preprocess(notebook, resources={'metadata': {'path': os.path.abspath(os.path.dirname(notebook_path))}}) except CellExecutionError as err: print('Error while processing notebook.') print(err) exporter = RSTExporter() output, resources = exporter.from_notebook_node(notebook, resources) writer = FilesWriter(build_directory=os.path.dirname(export_path)) writer.write(output, resources, notebook_name=os.path.basename(export_path)) pictures = sorted(resources['outputs'], key=output.find) try: thumbnail_source = pictures[thumbnail_figure_index] # Read in thumbnail source image img = Image.open(os.path.dirname(export_path) + '/' + thumbnail_source) # Trim whitespace bg = Image.new(img.mode, img.size, img.getpixel((0, 0))) diff = ImageChops.difference(img, bg) diff = ImageChops.add(diff, diff) bbox = diff.getbbox() if bbox: img = img.crop(bbox) # Resize image to have a width of 400px img.thumbnail([400, 1000]) # Save thumbnail img.save(thumb_dest) except: shutil.copyfile('_static/no_thumb.png', thumb_dest) print(' Done!') return title, level, description, thumb_dest.split('/', 1)[-1]