##// END OF EJS Templates
Moving things around.
Brian E. Granger -
Show More
@@ -0,0 +1,496 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "heading",
12 "level": 1,
13 "metadata": {},
14 "source": [
15 "NbConvert"
16 ]
17 },
18 {
19 "cell_type": "heading",
20 "level": 2,
21 "metadata": {},
22 "source": [
23 "Command line usage"
24 ]
25 },
26 {
27 "cell_type": "markdown",
28 "metadata": {},
29 "source": [
30 "`NbConvert` is the library, and the command line tool that allow to convert from notebook to other formats.\n",
31 "It is a technological preview in 1.0 but is already usable and highly configurable.\n",
32 "It ships already with many default available formats : `html`, `latex`, `markdown`, `python`, `rst` and `slides`\n",
33 "which are fully base on Jinja templating engine, so writing a converter for your custom format or tweeking the existing \n",
34 "one should be extra simple."
35 ]
36 },
37 {
38 "cell_type": "markdown",
39 "metadata": {},
40 "source": [
41 "You can invoke nbconvert by doing\n",
42 "\n",
43 "```bash\n",
44 "$ ipython nbconvert <options and arguments>\n",
45 "```\n",
46 "\n",
47 "Call `ipython nbconvert` with the `--help` flag or no aruments to get basic help on how to use it.\n",
48 "For more information about configuration use the `--help-all` flag"
49 ]
50 },
51 {
52 "cell_type": "heading",
53 "level": 3,
54 "metadata": {},
55 "source": [
56 "Basic export"
57 ]
58 },
59 {
60 "cell_type": "markdown",
61 "metadata": {},
62 "source": [
63 "We will be converting `Custom Display Logic.ipynb`. \n",
64 "Be sure to have runed some of the cells in it to have output otherwise you will only see input in nbconvert.\n",
65 "Nbconvert **do not execute the code** in the notebook files, it only converts what is inside."
66 ]
67 },
68 {
69 "cell_type": "code",
70 "collapsed": false,
71 "input": [
72 "%%bash\n",
73 "ipython nbconvert '04 - Custom Display Logic.ipynb'"
74 ],
75 "language": "python",
76 "metadata": {},
77 "outputs": [
78 {
79 "output_type": "stream",
80 "stream": "stderr",
81 "text": [
82 "[NbConvertApp] Using existing profile dir: u'/Users/bussonniermatthias/.ipython/profile_default'\n",
83 "[NbConvertApp] Converting notebook 04 - Custom Display Logic.ipynb to html\n",
84 "[NbConvertApp] Support files will be in 04 - Custom Display Logic_files/\n",
85 "[NbConvertApp] Loaded template html_full.tpl\n",
86 "[NbConvertApp] Writing 221081 bytes to 04 - Custom Display Logic.html\n"
87 ]
88 }
89 ],
90 "prompt_number": 1
91 },
92 {
93 "cell_type": "markdown",
94 "metadata": {},
95 "source": [
96 "Html is the default value (that can be configured) , so the verbose form would be "
97 ]
98 },
99 {
100 "cell_type": "code",
101 "collapsed": false,
102 "input": [
103 "%%bash\n",
104 "ipython nbconvert --to=html '04 - Custom Display Logic.ipynb'"
105 ],
106 "language": "python",
107 "metadata": {},
108 "outputs": [
109 {
110 "output_type": "stream",
111 "stream": "stderr",
112 "text": [
113 "[NbConvertApp] Using existing profile dir: u'/Users/bussonniermatthias/.ipython/profile_default'\n",
114 "[NbConvertApp] Converting notebook 04 - Custom Display Logic.ipynb to html\n",
115 "[NbConvertApp] Support files will be in 04 - Custom Display Logic_files/\n",
116 "[NbConvertApp] Loaded template html_full.tpl\n",
117 "[NbConvertApp] Writing 221081 bytes to 04 - Custom Display Logic.html\n"
118 ]
119 }
120 ],
121 "prompt_number": 2
122 },
123 {
124 "cell_type": "markdown",
125 "metadata": {},
126 "source": [
127 "You can also convert to latex, which will take care of extractin the embeded base64 encoded png, or the svg and call inkscape to convert those svg to pdf if necessary :"
128 ]
129 },
130 {
131 "cell_type": "code",
132 "collapsed": false,
133 "input": [
134 "%%bash\n",
135 "ipython nbconvert --to=latex '04 - Custom Display Logic.ipynb'"
136 ],
137 "language": "python",
138 "metadata": {},
139 "outputs": [
140 {
141 "output_type": "stream",
142 "stream": "stderr",
143 "text": [
144 "[NbConvertApp] Using existing profile dir: u'/Users/bussonniermatthias/.ipython/profile_default'\n",
145 "[NbConvertApp] Converting notebook 04 - Custom Display Logic.ipynb to latex\n",
146 "[NbConvertApp] Support files will be in 04 - Custom Display Logic_files/\n",
147 "Setting Language: .UTF-8\n",
148 "\n",
149 "(process:26432): Gtk-WARNING **: Locale not supported by C library.\n",
150 "\tUsing the fallback 'C' locale.\n",
151 "Setting Language: .UTF-8\n",
152 "\n",
153 "(process:26472): Gtk-WARNING **: Locale not supported by C library.\n",
154 "\tUsing the fallback 'C' locale.\n",
155 "Setting Language: .UTF-8\n",
156 "\n",
157 "(process:26512): Gtk-WARNING **: Locale not supported by C library.\n",
158 "\tUsing the fallback 'C' locale.\n",
159 "Setting Language: .UTF-8\n",
160 "\n",
161 "(process:26552): Gtk-WARNING **: Locale not supported by C library.\n",
162 "\tUsing the fallback 'C' locale.\n",
163 "Setting Language: .UTF-8\n",
164 "\n",
165 "(process:26592): Gtk-WARNING **: Locale not supported by C library.\n",
166 "\tUsing the fallback 'C' locale.\n",
167 "[NbConvertApp] Loaded template latex_article.tplx\n",
168 "[NbConvertApp] Writing 41196 bytes to 04 - Custom Display Logic.tex\n"
169 ]
170 }
171 ],
172 "prompt_number": 3
173 },
174 {
175 "cell_type": "markdown",
176 "metadata": {},
177 "source": [
178 "You should just have to compile the generated `.tex` file. If you get the required packages installed, if should compile out of the box.\n",
179 "\n",
180 "For convenience we allow to run extra action after the conversion has been done, in particular for `latex` we have a `pdf` post-processor. \n",
181 "You can define the postprocessor tu run with the `--post` flag."
182 ]
183 },
184 {
185 "cell_type": "code",
186 "collapsed": false,
187 "input": [
188 "%%bash\n",
189 "ipython nbconvert --to=latex '04 - Custom Display Logic.ipynb' --post=pdf"
190 ],
191 "language": "python",
192 "metadata": {},
193 "outputs": [
194 {
195 "output_type": "stream",
196 "stream": "stderr",
197 "text": [
198 "[NbConvertApp] Using existing profile dir: u'/Users/bussonniermatthias/.ipython/profile_default'\n",
199 "[NbConvertApp] Converting notebook 04 - Custom Display Logic.ipynb to latex\n",
200 "[NbConvertApp] Support files will be in 04 - Custom Display Logic_files/\n",
201 "Setting Language: .UTF-8\n",
202 "\n",
203 "(process:26658): Gtk-WARNING **: Locale not supported by C library.\n",
204 "\tUsing the fallback 'C' locale.\n",
205 "Setting Language: .UTF-8\n",
206 "\n",
207 "(process:26698): Gtk-WARNING **: Locale not supported by C library.\n",
208 "\tUsing the fallback 'C' locale.\n",
209 "Setting Language: .UTF-8\n",
210 "\n",
211 "(process:26738): Gtk-WARNING **: Locale not supported by C library.\n",
212 "\tUsing the fallback 'C' locale.\n",
213 "Setting Language: .UTF-8\n",
214 "\n",
215 "(process:26778): Gtk-WARNING **: Locale not supported by C library.\n",
216 "\tUsing the fallback 'C' locale.\n",
217 "Setting Language: .UTF-8\n",
218 "\n",
219 "(process:26818): Gtk-WARNING **: Locale not supported by C library.\n",
220 "\tUsing the fallback 'C' locale.\n",
221 "[NbConvertApp] Loaded template latex_article.tplx\n",
222 "[NbConvertApp] Writing 41196 bytes to 04 - Custom Display Logic.tex\n",
223 "[NbConvertApp] Building PDF: ['pdflatex', '04 - Custom Display Logic.tex']\n"
224 ]
225 }
226 ],
227 "prompt_number": 4
228 },
229 {
230 "cell_type": "markdown",
231 "metadata": {},
232 "source": [
233 "Have a look at `04 - Custom Display Logic.pdf`, toward the end, where we compared `display()` vs `display_html()` and returning the object.\n",
234 "See how the cell where we use `display_html` was not able to display the circle, whereas the two other ones were able to select one of the oher representation they know how to display."
235 ]
236 },
237 {
238 "cell_type": "heading",
239 "level": 3,
240 "metadata": {},
241 "source": [
242 "Customizing template"
243 ]
244 },
245 {
246 "cell_type": "markdown",
247 "metadata": {},
248 "source": [
249 "let's look at the first 20 lines of the `python` exporter"
250 ]
251 },
252 {
253 "cell_type": "code",
254 "collapsed": false,
255 "input": [
256 "pyfile = !ipython nbconvert --to python '04 - Custom Display Logic.ipynb' --stdout\n",
257 "for l in pyfile[20:40]:\n",
258 " print l"
259 ],
260 "language": "python",
261 "metadata": {},
262 "outputs": [
263 {
264 "output_type": "stream",
265 "stream": "stdout",
266 "text": [
267 "# 1. Implementing special display methods such as `_repr_html_`.\n",
268 "# 2. Registering a display function for a particular type.\n",
269 "# \n",
270 "# In this Notebook we show how both approaches work.\n",
271 "\n",
272 "# Before we get started, we will import the various display functions for displaying the different formats we will create.\n",
273 "\n",
274 "# In[54]:\n",
275 "\n",
276 "from IPython.display import display\n",
277 "from IPython.display import (\n",
278 " display_html, display_jpeg, display_png,\n",
279 " display_javascript, display_svg, display_latex\n",
280 ")\n",
281 "\n",
282 "\n",
283 "### Implementing special display methods\n",
284 "\n",
285 "# The main idea of the first approach is that you have to implement special display methods, one for each representation you want to use. Here is a list of the names of the special methods and the values they must return:\n",
286 "# \n"
287 ]
288 }
289 ],
290 "prompt_number": 5
291 },
292 {
293 "cell_type": "markdown",
294 "metadata": {},
295 "source": [
296 "We see that the non-code cell are exported to the file. To have a cleaner script, we will export only the code contained in the code cells.\n",
297 "\n",
298 "To do so, we will inherit the python template, and overwrite the markdown blocks to be empty."
299 ]
300 },
301 {
302 "cell_type": "code",
303 "collapsed": false,
304 "input": [
305 "%%writefile simplepython.tpl\n",
306 "{% extends 'python.tpl'%}\n",
307 "\n",
308 "{% block markdowncell -%}\n",
309 "{% endblock markdowncell %}\n",
310 "\n",
311 "## we also want to get rig of header cell\n",
312 "{% block headingcell -%}\n",
313 "{% endblock headingcell %}\n",
314 "\n",
315 "## and let's change the appearance of input prompt\n",
316 "{% block in_prompt %}\n",
317 "# This was input cell with prompt number : {{ cell.prompt_number if cell.prompt_number else ' ' }}\n",
318 "{%- endblock in_prompt %}"
319 ],
320 "language": "python",
321 "metadata": {},
322 "outputs": [
323 {
324 "output_type": "stream",
325 "stream": "stdout",
326 "text": [
327 "Overwriting simplepython.tpl\n"
328 ]
329 }
330 ],
331 "prompt_number": 6
332 },
333 {
334 "cell_type": "code",
335 "collapsed": false,
336 "input": [
337 "pyfile = !ipython nbconvert --to python '04 - Custom Display Logic.ipynb' --stdout --template=simplepython.tpl\n",
338 "\n",
339 "for l in pyfile[4:40]:\n",
340 " print l\n",
341 "print '...'"
342 ],
343 "language": "python",
344 "metadata": {},
345 "outputs": [
346 {
347 "output_type": "stream",
348 "stream": "stdout",
349 "text": [
350 "\n",
351 "# This was input cell with prompt number : 54\n",
352 "from IPython.display import display\n",
353 "from IPython.display import (\n",
354 " display_html, display_jpeg, display_png,\n",
355 " display_javascript, display_svg, display_latex\n",
356 ")\n",
357 "\n",
358 "\n",
359 "# This was input cell with prompt number : 55\n",
360 "get_ipython().magic(u'load soln/mycircle.py')\n",
361 "\n",
362 "\n",
363 "# This was input cell with prompt number : 56\n",
364 "class MyCircle(object):\n",
365 " \n",
366 " def _repr_html_(self):\n",
367 " return \"&#x25CB; (<b>html</b>)\"\n",
368 "\n",
369 " def _repr_svg_(self):\n",
370 " return \"\"\"<svg width='100px' height='100px'>\n",
371 " <circle cx=\"50\" cy=\"50\" r=\"20\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\"/>\n",
372 " </svg>\"\"\"\n",
373 " \n",
374 " def _repr_latex_(self):\n",
375 " return r\"$\\bigcirc \\LaTeX$\"\n",
376 "\n",
377 " def _repr_javascript_(self):\n",
378 " return \"alert('I am a circle!');\"\n",
379 "\n",
380 "\n",
381 "# This was input cell with prompt number : 57\n",
382 "c = MyCircle()\n",
383 "\n",
384 "\n",
385 "# This was input cell with prompt number : 58\n",
386 "...\n"
387 ]
388 }
389 ],
390 "prompt_number": 7
391 },
392 {
393 "cell_type": "markdown",
394 "metadata": {},
395 "source": [
396 "I'll let you read Jinja manual for the exact syntax of the template."
397 ]
398 },
399 {
400 "cell_type": "heading",
401 "level": 2,
402 "metadata": {},
403 "source": [
404 "Template that use cells metadata"
405 ]
406 },
407 {
408 "cell_type": "markdown",
409 "metadata": {},
410 "source": [
411 "Notebook fileformat support attaching arbitrary JSON metadata to each cell of a notebook. In this part we will use those metadata."
412 ]
413 },
414 {
415 "cell_type": "markdown",
416 "metadata": {},
417 "source": [
418 "First you need to choose another notebook you want to convert to html, and tag some of the cell with metadata.\n",
419 "You can see the file `soln/celldiff.js` for a solution on how to tag, or follow the javascript tutorial to see how to do that. Use what we have written there to tag cells of some notebooks to `Easy`|`Medium`|`Hard`|`<None>`, and convert this notebook using your template."
420 ]
421 },
422 {
423 "cell_type": "markdown",
424 "metadata": {},
425 "source": [
426 "you might need the following : \n",
427 "```\n",
428 "{% extends 'html_full.tpl'%}\n",
429 "{% block any_cell %}\n",
430 "{{ super() }}\n",
431 "<div style=\"background-color:red\">\n",
432 "<div style='background-color:orange'>\n",
433 "```\n",
434 "\n",
435 "`metadata` might not exist, be sure to :\n",
436 "\n",
437 "`cell['metadata'].get('example',{}).get('difficulty','')`\n",
438 "\n",
439 "tip: use `%%writefile` to edit the template in the notebook :-)"
440 ]
441 },
442 {
443 "cell_type": "code",
444 "collapsed": false,
445 "input": [
446 "%%bash\n",
447 "# ipython nbconvert --to html <your chosen notebook.ipynb> --template=<your template file>"
448 ],
449 "language": "python",
450 "metadata": {},
451 "outputs": [],
452 "prompt_number": 8
453 },
454 {
455 "cell_type": "code",
456 "collapsed": false,
457 "input": [
458 "%loadpy soln/coloreddiff.tpl"
459 ],
460 "language": "python",
461 "metadata": {},
462 "outputs": []
463 },
464 {
465 "cell_type": "code",
466 "collapsed": false,
467 "input": [
468 "# ipython nbconvert --to html '04 - Custom Display Logic.ipynb' --template=soln/coloreddiff.tpl"
469 ],
470 "language": "python",
471 "metadata": {},
472 "outputs": []
473 },
474 {
475 "cell_type": "heading",
476 "level": 3,
477 "metadata": {},
478 "source": [
479 "Get rid of all command line flags."
480 ]
481 },
482 {
483 "cell_type": "markdown",
484 "metadata": {},
485 "source": [
486 "As of all of IPython nbconvert can be configured using profiles and passing the `--profile` flag. \n",
487 "Moreover if a `config.py` file exist in current working directory nbconvert will use that, or read the config file you give to it with the `--config=<file>` flag. \n",
488 "\n",
489 "In the end, if you are often running nbconvert on the sam project, `$ ipython nbconvert` should be enough to get you up and ready."
490 ]
491 }
492 ],
493 "metadata": {}
494 }
495 ]
496 } No newline at end of file
This diff has been collapsed as it changes many lines, (1054 lines changed) Show them Hide them
@@ -0,0 +1,1054 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "heading",
12 "level": 1,
13 "metadata": {},
14 "source": [
15 "NbConvert, Python library"
16 ]
17 },
18 {
19 "cell_type": "markdown",
20 "metadata": {},
21 "source": [
22 "In this Notebook, I will introduce you to the programatic API of nbconvert to show you how to use it in various context. \n",
23 "\n",
24 "For this I will use one of [@jakevdp](https://github.com/jakevdp) great [blog post](http://jakevdp.github.io/blog/2013/04/15/code-golf-in-python-sudoku/).\n",
25 "I've explicitely chosen a post with no javascript tricks as Jake seem to be found of right now, for the reason that the becommings of embeding javascript in nbviewer, which is based on nbconvert is not fully decided yet. \n",
26 "\n",
27 "\n",
28 "This will not focus on using the command line tool to convert file. The attentive reader will point-out that no data are read from, or written to disk during the conversion process. Indeed, nbconvert as been though as much as\n",
29 "possible to avoid IO operation and work as well in a database, or web-based environement."
30 ]
31 },
32 {
33 "cell_type": "heading",
34 "level": 4,
35 "metadata": {},
36 "source": [
37 "Quick overview"
38 ]
39 },
40 {
41 "cell_type": "markdown",
42 "metadata": {},
43 "source": [
44 "<pre style='background-color:#FDD'> Warning, Do use 1.0 or 1.x branch and not master naming have changed.</pre>\n",
45 "\n",
46 "<pre style='background-color:#FDD'> Warning, NbConvert is a Tech-Preview, API will change within the next 6 month.</pre>"
47 ]
48 },
49 {
50 "cell_type": "markdown",
51 "metadata": {},
52 "source": [
53 "Credit, Jonathan Freder (@jdfreder on github)\n",
54 "\n",
55 "<center>\n",
56 " ![nbca](files/nbconvert_arch.png)\n",
57 "</center>"
58 ]
59 },
60 {
61 "cell_type": "markdown",
62 "metadata": {},
63 "source": [
64 "The main principle of nbconvert is to instanciate a `Exporter` that controle\n",
65 "a pipeline through which each notebook you want to export with go through."
66 ]
67 },
68 {
69 "cell_type": "markdown",
70 "metadata": {},
71 "source": [
72 "Let's start by importing what we need from the API, and download @jakevdp's notebook."
73 ]
74 },
75 {
76 "cell_type": "code",
77 "collapsed": false,
78 "input": [
79 "import requests\n",
80 "response = requests.get('http://jakevdp.github.com/downloads/notebooks/XKCD_plots.ipynb')\n",
81 "response.content[0:60]+'...'"
82 ],
83 "language": "python",
84 "metadata": {},
85 "outputs": [
86 {
87 "metadata": {},
88 "output_type": "pyout",
89 "prompt_number": 1,
90 "text": [
91 "'{\\n \"metadata\": {\\n \"name\": \"XKCD_plots\"\\n },\\n \"nbformat\": 3,\\n...'"
92 ]
93 }
94 ],
95 "prompt_number": 1
96 },
97 {
98 "cell_type": "markdown",
99 "metadata": {},
100 "source": [
101 "If you do not have request install downlad by hand, and read the file as usual."
102 ]
103 },
104 {
105 "cell_type": "markdown",
106 "metadata": {},
107 "source": [
108 "We read the response into a slightly more convenient format which represent IPython notebook. \n",
109 "There are not real advantages for now, except some convenient methods, but with time this structure should be able to\n",
110 "guarantee that the notebook structure is valid. Note also that the in-memory format and on disk format can be slightly different. In particual, on disk, multiline strings might be spitted into list of string to be more version control friendly."
111 ]
112 },
113 {
114 "cell_type": "code",
115 "collapsed": false,
116 "input": [
117 "from IPython.nbformat import current as nbformat\n",
118 "jake_notebook = nbformat.reads_json(response.content)\n",
119 "jake_notebook.worksheets[0].cells[0]"
120 ],
121 "language": "python",
122 "metadata": {},
123 "outputs": [
124 {
125 "metadata": {},
126 "output_type": "pyout",
127 "prompt_number": 2,
128 "text": [
129 "{u'cell_type': u'heading',\n",
130 " u'level': 1,\n",
131 " u'metadata': {},\n",
132 " u'source': u'XKCD plots in Matplotlib'}"
133 ]
134 }
135 ],
136 "prompt_number": 2
137 },
138 {
139 "cell_type": "markdown",
140 "metadata": {},
141 "source": [
142 "So we have here Jake's notebook in a convenient form, which is mainly a Super-Powered dict and list nested.\n",
143 "You don't need to worry about the exact structure."
144 ]
145 },
146 {
147 "cell_type": "markdown",
148 "metadata": {},
149 "source": [
150 "The nbconvert API exposes some basic exporter for common format and default options. We will start\n",
151 "by using one of them. First we import it, instanciate an instance with most of the default parameters and fed it\n",
152 "the downloaded notebook. "
153 ]
154 },
155 {
156 "cell_type": "code",
157 "collapsed": false,
158 "input": [
159 "import IPython.nbconvert"
160 ],
161 "language": "python",
162 "metadata": {},
163 "outputs": [],
164 "prompt_number": 3
165 },
166 {
167 "cell_type": "code",
168 "collapsed": false,
169 "input": [
170 "from IPython.config import Config\n",
171 "from IPython.nbconvert import HTMLExporter\n",
172 "\n",
173 "## I use `basic` here to have less boilerplate and headers in the HTML.\n",
174 "## we'll see later how to pass config to exporters.\n",
175 "exportHtml = HTMLExporter(config=Config({'HTMLExporter':{'default_template':'basic'}}))"
176 ],
177 "language": "python",
178 "metadata": {},
179 "outputs": [],
180 "prompt_number": 4
181 },
182 {
183 "cell_type": "code",
184 "collapsed": false,
185 "input": [
186 "(body, resources) = exportHtml.from_notebook_node(jake_notebook)"
187 ],
188 "language": "python",
189 "metadata": {},
190 "outputs": [],
191 "prompt_number": 5
192 },
193 {
194 "cell_type": "markdown",
195 "metadata": {},
196 "source": [
197 "The exporter returns a tuple containing the body of the converted notebook, here raw HTML, as well as a resources dict.\n",
198 "The resource dict contains (among many things) the extracted PNG, JPG [...etc] from the notebook when applicable.\n",
199 "The basic HTML exporter does keep them as embeded base64 into the notebook, but one can do ask the figures to be extracted. Cf advance use. So for now the resource dict **should** be mostly empty, except for 1 key containing some css, and 2 others whose content will be obvious.\n",
200 "\n",
201 "Exporter are stateless, you won't be able to extract any usefull information (except their configuration) from them.\n",
202 "You can directly re-use the instance to convert another notebook. Each exporter expose for convenience a `from_file` and `from_filename` methods if you need."
203 ]
204 },
205 {
206 "cell_type": "code",
207 "collapsed": false,
208 "input": [
209 "print resources.keys()\n",
210 "print resources['metadata']\n",
211 "print resources['output_extension']\n",
212 "# print resources['inlining'] # too lng to be shown"
213 ],
214 "language": "python",
215 "metadata": {},
216 "outputs": [
217 {
218 "output_type": "stream",
219 "stream": "stdout",
220 "text": [
221 "['inlining', 'output_extension', 'metadata']\n",
222 "defaultdict(None, {'name': 'Notebook'})\n",
223 "html\n"
224 ]
225 }
226 ],
227 "prompt_number": 6
228 },
229 {
230 "cell_type": "code",
231 "collapsed": false,
232 "input": [
233 "# Part of the body, here the first Heading\n",
234 "start = body.index('<h1 id', )\n",
235 "print body[:400]+'...'"
236 ],
237 "language": "python",
238 "metadata": {},
239 "outputs": [
240 {
241 "output_type": "stream",
242 "stream": "stdout",
243 "text": [
244 "\n",
245 "<div class=\"text_cell_render border-box-sizing rendered_html\">\n",
246 "<h1 id=\"XKCD-plots-in-Matplotlib\">XKCD plots in Matplotlib<a class=\"anchor-link\" href=\"#XKCD-plots-in-Matplotlib\">&#182;</a></h1>\n",
247 "</div>\n",
248 "\n",
249 "<div class=\"text_cell_render border-box-sizing rendered_html\">\n",
250 "<p>This notebook originally appeared as a blog post at <a href=\"http://jakevdp.github.com/blog/2012/10/07/xkcd-style-plots-in-matplotli...\n"
251 ]
252 }
253 ],
254 "prompt_number": 7
255 },
256 {
257 "cell_type": "markdown",
258 "metadata": {},
259 "source": [
260 "You can directly write the body into an HTML file if you wish, as you see it does not contains any body tag, or style declaration, but thoses are included in the default HtmlExporter if you do not pass it a config object as I did."
261 ]
262 },
263 {
264 "cell_type": "heading",
265 "level": 4,
266 "metadata": {},
267 "source": [
268 "Extracting Figures"
269 ]
270 },
271 {
272 "cell_type": "markdown",
273 "metadata": {},
274 "source": [
275 "When exporting one might want to extract the base64 encoded figures to separate files, this is by default what does the RstExporter does, let see how to use it. "
276 ]
277 },
278 {
279 "cell_type": "code",
280 "collapsed": false,
281 "input": [
282 "from IPython.nbconvert import RSTExporter\n",
283 "\n",
284 "rst_export = RSTExporter()\n",
285 "\n",
286 "(body,resources) = rst_export.from_notebook_node(jake_notebook)"
287 ],
288 "language": "python",
289 "metadata": {},
290 "outputs": [],
291 "prompt_number": 8
292 },
293 {
294 "cell_type": "code",
295 "collapsed": false,
296 "input": [
297 "print body[:970]+'...'\n",
298 "print '[.....]'\n",
299 "print body[800:1200]+'...'"
300 ],
301 "language": "python",
302 "metadata": {},
303 "outputs": [
304 {
305 "output_type": "stream",
306 "stream": "stdout",
307 "text": [
308 "\n",
309 "XKCD plots in Matplotlib\n",
310 "========================\n",
311 "\n",
312 "\n",
313 "This notebook originally appeared as a blog post at `Pythonic\n",
314 "Perambulations <http://jakevdp.github.com/blog/2012/10/07/xkcd-style-plots-in-matplotlib/>`_\n",
315 "by Jake Vanderplas.\n",
316 "\n",
317 " *Update: the matplotlib pull request has been merged! See* `*This\n",
318 "post* <http://jakevdp.github.io/blog/2013/07/10/XKCD-plots-in-matplotlib/>`_\n",
319 "*for a description of the XKCD functionality now built-in to\n",
320 "matplotlib!*\n",
321 "\n",
322 "One of the problems I've had with typical matplotlib figures is that\n",
323 "everything in them is so precise, so perfect. For an example of what I\n",
324 "mean, take a look at this figure:\n",
325 "In[1]:\n",
326 ".. code:: python\n",
327 "\n",
328 " from IPython.display import Image\n",
329 " Image('http://jakevdp.github.com/figures/xkcd_version.png')\n",
330 "\n",
331 "\n",
332 "\n",
333 "\n",
334 "\n",
335 ".. image:: output_3_0.png\n",
336 "\n",
337 "\n",
338 "\n",
339 "Sometimes when showing schematic plots, this is the type of figure I\n",
340 "want to display. But drawing it by hand is a pain: I'd rather just use\n",
341 "matplotlib. The problem is, matplotlib is a bit...\n",
342 "[.....]\n",
343 "owing schematic plots, this is the type of figure I\n",
344 "want to display. But drawing it by hand is a pain: I'd rather just use\n",
345 "matplotlib. The problem is, matplotlib is a bit too precise. Attempting\n",
346 "to duplicate this figure in matplotlib leads to something like this:\n",
347 "In[2]:\n",
348 ".. code:: python\n",
349 "\n",
350 " Image('http://jakevdp.github.com/figures/mpl_version.png')\n",
351 "\n",
352 "\n",
353 "\n",
354 "\n",
355 "\n",
356 ".. image:: output_5_0.png\n",
357 "\n",
358 "\n",
359 "\n",
360 "It just doesn'...\n"
361 ]
362 }
363 ],
364 "prompt_number": 9
365 },
366 {
367 "cell_type": "markdown",
368 "metadata": {},
369 "source": [
370 "Here we see that base64 images are not embeded, but we get what look like file name. Actually those are (Configurable) keys to get back the binary data from the resources dict we havent inspected earlier.\n"
371 ]
372 },
373 {
374 "cell_type": "markdown",
375 "metadata": {},
376 "source": [
377 "So when writing a Rst Plugin for any blogengine, Sphinx or anything else, you will be responsible for writing all those data to disk, in the right place. \n",
378 "Of course to help you in this task all those naming are configurable in the right place."
379 ]
380 },
381 {
382 "cell_type": "markdown",
383 "metadata": {},
384 "source": [
385 "let's try to see how to get one of these images"
386 ]
387 },
388 {
389 "cell_type": "code",
390 "collapsed": false,
391 "input": [
392 "resources['outputs'].keys()"
393 ],
394 "language": "python",
395 "metadata": {},
396 "outputs": [
397 {
398 "metadata": {},
399 "output_type": "pyout",
400 "prompt_number": 10,
401 "text": [
402 "[u'output_13_1.text',\n",
403 " u'output_18_0.text',\n",
404 " u'output_3_0.text',\n",
405 " u'output_18_1.png',\n",
406 " u'output_12_0.text',\n",
407 " u'output_5_0.text',\n",
408 " u'output_5_0.png',\n",
409 " u'output_13_1.png',\n",
410 " u'output_16_0.text',\n",
411 " u'output_13_0.text',\n",
412 " u'output_18_1.text',\n",
413 " u'output_3_0.png',\n",
414 " u'output_16_0.png']"
415 ]
416 }
417 ],
418 "prompt_number": 10
419 },
420 {
421 "cell_type": "markdown",
422 "metadata": {},
423 "source": [
424 "We have extracted 5 binary figures, here `png`s, but they could have been svg, and then wouldn't appear in the binary sub dict.\n",
425 "keep in mind that a object having multiple _repr_ will store all it's repr in the notebook. \n",
426 "\n",
427 "Hence if you provide `_repr_javascript_`,`_repr_latex_` and `_repr_png_`to an object, you will be able to determine at conversion time which representaition is the more appropriate. You could even decide to show all the representaition of an object, it's up to you. But this will require beeing a little more involve and write a few line of Jinja template. This will probably be the subject of another tutorial.\n",
428 "\n",
429 "Back to our images,\n",
430 "\n"
431 ]
432 },
433 {
434 "cell_type": "code",
435 "collapsed": false,
436 "input": [
437 "from IPython.display import Image\n",
438 "Image(data=resources['outputs']['output_3_0.png'],format='png')"
439 ],
440 "language": "python",
441 "metadata": {},
442 "outputs": [
443 {
444 "metadata": {},
445 "output_type": "pyout",
446 "png": "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\nRU5ErkJggg==\n",
447 "prompt_number": 11,
448 "text": [
449 "<IPython.core.display.Image at 0x10d2bbf90>"
450 ]
451 }
452 ],
453 "prompt_number": 11
454 },
455 {
456 "cell_type": "markdown",
457 "metadata": {},
458 "source": [
459 "Yep, this is indeed the image we were expecting, and I was able to see it without ever writing or reading it from disk. I don't think I'll have to show to you what to do with those data, as if you are here you are most probably familiar with IO."
460 ]
461 },
462 {
463 "cell_type": "heading",
464 "level": 2,
465 "metadata": {},
466 "source": [
467 "Extracting figures with HTML Exporter ?"
468 ]
469 },
470 {
471 "cell_type": "markdown",
472 "metadata": {},
473 "source": [
474 "Use case:\n",
475 "\n",
476 "> I write an [awesome blog](http://jakevdp.github.io/) in HTML, and I want all but having base64 embeded images. \n",
477 "Having one html file with all inside is nice to send to coworker, but I definitively want resources to be cached !\n",
478 "So I need an HTML exporter, and I want it to extract the figures !"
479 ]
480 },
481 {
482 "cell_type": "heading",
483 "level": 3,
484 "metadata": {},
485 "source": [
486 "Some theory"
487 ]
488 },
489 {
490 "cell_type": "markdown",
491 "metadata": {},
492 "source": [
493 "The process of converting a notebook to a another format with the nbconvert Exporters happend in a few steps:\n",
494 "\n",
495 " - Get the notebook data and other required files. (you are responsible for that)\n",
496 " - Feed them to the exporter that will\n",
497 " - sequentially feed the data to a number of `Transformers`. Transformer only act on the **structure**\n",
498 " of the notebook, and have access to it all. \n",
499 " - feed the notebook through the jinja templating engine\n",
500 " - the use templates are configurable.\n",
501 " - templates make use of configurable macros called filters.\n",
502 " - The exporter return the converted notebook as well as other relevant resources as a tuple.\n",
503 " - Write what you need to disk, or elsewhere. (You are responsible for it)"
504 ]
505 },
506 {
507 "cell_type": "markdown",
508 "metadata": {},
509 "source": [
510 "Here we'll be interested in the `Transformers`. Each `Transformer` is applied successively and in order on the notebook before going through the conversion process.\n",
511 "\n",
512 "We provide some transformer that do some modification on the notebook structure by default.\n",
513 "One of them, the `ExtractOutputTransformer` is responsible for crawling notebook,\n",
514 "finding all the figures, and put them into the resources directory, as well as choosing the key\n",
515 "(`filename_xx_y.extension`) that can replace the figure in the template.\n",
516 "\n",
517 "\n",
518 "The `ExtractOutputTransformer` is special in the fact that it **should** be availlable on all `Exporter`s, but is just inactive by default on some exporter."
519 ]
520 },
521 {
522 "cell_type": "code",
523 "collapsed": false,
524 "input": [
525 "# second transformer shoudl be Instance of ExtractFigureTransformer\n",
526 "exportHtml._transformers # 3rd one shouel be <ExtractOutputTransformer>"
527 ],
528 "language": "python",
529 "metadata": {},
530 "outputs": [
531 {
532 "metadata": {},
533 "output_type": "pyout",
534 "prompt_number": 12,
535 "text": [
536 "[<function IPython.nbconvert.transformers.coalescestreams.wrappedfunc>,\n",
537 " <IPython.nbconvert.transformers.svg2pdf.SVG2PDFTransformer at 0x10d2a7490>,\n",
538 " <IPython.nbconvert.transformers.extractoutput.ExtractOutputTransformer at 0x10d2a7ad0>,\n",
539 " <IPython.nbconvert.transformers.csshtmlheader.CSSHTMLHeaderTransformer at 0x10d2a7b50>,\n",
540 " <IPython.nbconvert.transformers.revealhelp.RevealHelpTransformer at 0x10d29dd90>,\n",
541 " <IPython.nbconvert.transformers.latex.LatexTransformer at 0x10d29db50>,\n",
542 " <IPython.nbconvert.transformers.sphinx.SphinxTransformer at 0x10d2a7b90>]"
543 ]
544 }
545 ],
546 "prompt_number": 12
547 },
548 {
549 "cell_type": "markdown",
550 "metadata": {},
551 "source": [
552 "To enable it we will use IPython configuration/Traitlets system. If you are have already set some IPython configuration options, \n",
553 "this will look pretty familiar to you. Configuration option are always of the form:\n",
554 "\n",
555 " ClassName.attribute_name = value\n",
556 " \n",
557 "A few ways exist to create such config, like reading a config file in your profile, but you can also do it programatically usign a dictionary. Let's create such a config object, and see the difference if we pass it to our `HtmlExporter`"
558 ]
559 },
560 {
561 "cell_type": "code",
562 "collapsed": false,
563 "input": [
564 "from IPython.config import Config\n",
565 "\n",
566 "c = Config({\n",
567 " 'ExtractOutputTransformer':{'enabled':True}\n",
568 " })\n",
569 "\n",
570 "exportHtml = HTMLExporter()\n",
571 "exportHtml_and_figs = HTMLExporter(config=c)\n",
572 "\n",
573 "(_, resources) = exportHtml.from_notebook_node(jake_notebook)\n",
574 "(_, resources_with_fig) = exportHtml_and_figs.from_notebook_node(jake_notebook)\n",
575 "\n",
576 "print 'resources without the \"figures\" key :'\n",
577 "print resources.keys()\n",
578 "\n",
579 "print ''\n",
580 "print 'Here we have one more field '\n",
581 "print resources_with_fig.keys()\n",
582 "resources_with_fig['outputs'].keys() "
583 ],
584 "language": "python",
585 "metadata": {},
586 "outputs": [
587 {
588 "output_type": "stream",
589 "stream": "stdout",
590 "text": [
591 "resources without the \"figures\" key :\n",
592 "['inlining', 'output_extension', 'metadata']\n",
593 "\n",
594 "Here we have one more field \n",
595 "['outputs', 'inlining', 'output_extension', 'metadata']\n"
596 ]
597 },
598 {
599 "metadata": {},
600 "output_type": "pyout",
601 "prompt_number": 13,
602 "text": [
603 "[u'output_13_1.text',\n",
604 " u'output_18_0.text',\n",
605 " u'output_3_0.text',\n",
606 " u'output_18_1.png',\n",
607 " u'output_12_0.text',\n",
608 " u'output_5_0.text',\n",
609 " u'output_5_0.png',\n",
610 " u'output_13_1.png',\n",
611 " u'output_16_0.text',\n",
612 " u'output_13_0.text',\n",
613 " u'output_18_1.text',\n",
614 " u'output_3_0.png',\n",
615 " u'output_16_0.png']"
616 ]
617 }
618 ],
619 "prompt_number": 13
620 },
621 {
622 "cell_type": "markdown",
623 "metadata": {},
624 "source": [
625 "So now you can loop through the dict and write all those figures to disk in the right place... "
626 ]
627 },
628 {
629 "cell_type": "heading",
630 "level": 4,
631 "metadata": {},
632 "source": [
633 "Custom transformer"
634 ]
635 },
636 {
637 "cell_type": "markdown",
638 "metadata": {},
639 "source": [
640 "Of course you can imagine many transformation that you would like to apply to a notebook. This is one of the reason we provide a way to register your own transformers that will be applied to the notebook after the default ones.\n",
641 "\n",
642 "To do so you'll have to pass an ordered list of `Transformer`s to the Exporter constructor. \n",
643 "\n",
644 "But what is an transformer ? Transformer can be either *decorated function* for dead-simple `Transformer`s that apply\n",
645 "independently to each cell, for more advance transformation that support configurability You have to inherit from\n",
646 "`Transformer` and define a `call` method as we'll see below.\n",
647 "\n",
648 "All transforers have a magic attribute that allows it to be activated/disactivate from the config dict."
649 ]
650 },
651 {
652 "cell_type": "code",
653 "collapsed": false,
654 "input": [
655 "from IPython.nbconvert.transformers import Transformer\n",
656 "import IPython.config\n",
657 "print \"Four relevant docstring\"\n",
658 "print '============================='\n",
659 "print Transformer.__doc__\n",
660 "print '============================='\n",
661 "print Transformer.call.__doc__\n",
662 "print '============================='\n",
663 "print Transformer.transform_cell.__doc__\n",
664 "print '============================='"
665 ],
666 "language": "python",
667 "metadata": {},
668 "outputs": [
669 {
670 "output_type": "stream",
671 "stream": "stdout",
672 "text": [
673 "Four relevant docstring\n",
674 "=============================\n",
675 " A configurable transformer\n",
676 "\n",
677 " Inherit from this class if you wish to have configurability for your\n",
678 " transformer.\n",
679 "\n",
680 " Any configurable traitlets this class exposed will be configurable in profiles\n",
681 " using c.SubClassName.atribute=value\n",
682 "\n",
683 " you can overwrite transform_cell to apply a transformation independently on each cell\n",
684 " or __call__ if you prefer your own logic. See corresponding docstring for informations.\n",
685 "\n",
686 " Disabled by default and can be enabled via the config by\n",
687 " 'c.YourTransformerName.enabled = True'\n",
688 " \n",
689 "=============================\n",
690 "\n",
691 " Transformation to apply on each notebook.\n",
692 " \n",
693 " You should return modified nb, resources.\n",
694 " If you wish to apply your transform on each cell, you might want to \n",
695 " overwrite transform_cell method instead.\n",
696 " \n",
697 " Parameters\n",
698 " ----------\n",
699 " nb : NotebookNode\n",
700 " Notebook being converted\n",
701 " resources : dictionary\n",
702 " Additional resources used in the conversion process. Allows\n",
703 " transformers to pass variables into the Jinja engine.\n",
704 " \n",
705 "=============================\n",
706 "\n",
707 " Overwrite if you want to apply a transformation on each cell. You \n",
708 " should return modified cell and resource dictionary.\n",
709 " \n",
710 " Parameters\n",
711 " ----------\n",
712 " cell : NotebookNode cell\n",
713 " Notebook cell being processed\n",
714 " resources : dictionary\n",
715 " Additional resources used in the conversion process. Allows\n",
716 " transformers to pass variables into the Jinja engine.\n",
717 " index : int\n",
718 " Index of the cell being processed\n",
719 " \n",
720 "=============================\n"
721 ]
722 }
723 ],
724 "prompt_number": 14
725 },
726 {
727 "cell_type": "markdown",
728 "metadata": {},
729 "source": [
730 "***\n",
731 "We don't provide convenient method to be aplied on each worksheet as the **data structure** for worksheet will be removed. (not the worksheet functionnality, which is still on it's way)\n",
732 "***"
733 ]
734 },
735 {
736 "cell_type": "heading",
737 "level": 3,
738 "metadata": {},
739 "source": [
740 "Example"
741 ]
742 },
743 {
744 "cell_type": "markdown",
745 "metadata": {},
746 "source": [
747 "I'll now demonstrate a specific example [requested](https://github.com/ipython/nbconvert/pull/137#issuecomment-18658235) while nbconvert 2 was beeing developped. The ability to exclude cell from the conversion process based on their index. \n",
748 "\n",
749 "I'll let you imagin how to inject cell, if what you just want is to happend static content at the beginning/end of a notebook, plese refer to templating section, it will be much easier and cleaner."
750 ]
751 },
752 {
753 "cell_type": "code",
754 "collapsed": false,
755 "input": [
756 "from IPython.utils.traitlets import Integer"
757 ],
758 "language": "python",
759 "metadata": {},
760 "outputs": [],
761 "prompt_number": 15
762 },
763 {
764 "cell_type": "code",
765 "collapsed": false,
766 "input": [
767 "class PelicanSubCell(Transformer):\n",
768 " \"\"\"A Pelican specific transformer to remove somme of the cells of a notebook\"\"\"\n",
769 " \n",
770 " # I could also read the cells from nbc.metadata.pelican is someone wrote a JS extension\n",
771 " # But I'll stay with configurable value. \n",
772 " start = Integer(0, config=True, help=\"first cell of notebook to be converted\")\n",
773 " end = Integer(-1, config=True, help=\"last cell of notebook to be converted\")\n",
774 " \n",
775 " def call(self, nb, resources):\n",
776 "\n",
777 " #nbc = deepcopy(nb)\n",
778 " nbc = nb\n",
779 " # don't print in real transformer !!!\n",
780 " print \"I'll keep only cells from \", self.start, \"to \", self.end, \"\\n\\n\"\n",
781 " for worksheet in nbc.worksheets :\n",
782 " cells = worksheet.cells[:]\n",
783 " worksheet.cells = cells[self.start:self.end] \n",
784 " return nbc, resources"
785 ],
786 "language": "python",
787 "metadata": {},
788 "outputs": [],
789 "prompt_number": 16
790 },
791 {
792 "cell_type": "code",
793 "collapsed": false,
794 "input": [
795 "# I create this on the fly, but this could be loaded from a DB, and config object support merging...\n",
796 "c = Config({\n",
797 " 'PelicanSubCell':{\n",
798 " 'enabled':True,\n",
799 " 'start':4,\n",
800 " 'end':6,\n",
801 " }\n",
802 " })"
803 ],
804 "language": "python",
805 "metadata": {},
806 "outputs": [],
807 "prompt_number": 17
808 },
809 {
810 "cell_type": "markdown",
811 "metadata": {},
812 "source": [
813 "I'm creating a pelican exporter that take `PelicanSubCell` extra transformers and a `config` object as parameter. This might seem redundant, but with configuration system you'll see that one can register an inactive transformer on all exporters and activate it at will form its config files and command line. "
814 ]
815 },
816 {
817 "cell_type": "code",
818 "collapsed": false,
819 "input": [
820 "pelican = RSTExporter(transformers=[PelicanSubCell], config=c)"
821 ],
822 "language": "python",
823 "metadata": {},
824 "outputs": [],
825 "prompt_number": 18
826 },
827 {
828 "cell_type": "code",
829 "collapsed": false,
830 "input": [
831 "print pelican.from_notebook_node(jake_notebook)[0]"
832 ],
833 "language": "python",
834 "metadata": {},
835 "outputs": [
836 {
837 "output_type": "stream",
838 "stream": "stdout",
839 "text": [
840 "I'll keep only cells from 4 to 6 \n",
841 "\n",
842 "\n",
843 "\n",
844 "Sometimes when showing schematic plots, this is the type of figure I\n",
845 "want to display. But drawing it by hand is a pain: I'd rather just use\n",
846 "matplotlib. The problem is, matplotlib is a bit too precise. Attempting\n",
847 "to duplicate this figure in matplotlib leads to something like this:\n",
848 "In[2]:\n",
849 ".. code:: python\n",
850 "\n",
851 " Image('http://jakevdp.github.com/figures/mpl_version.png')\n",
852 "\n",
853 "\n",
854 "\n",
855 "\n",
856 "\n",
857 ".. image:: output_5_0.png\n",
858 "\n",
859 "\n"
860 ]
861 },
862 {
863 "output_type": "stream",
864 "stream": "stdout",
865 "text": [
866 "\n"
867 ]
868 }
869 ],
870 "prompt_number": 19
871 },
872 {
873 "cell_type": "heading",
874 "level": 3,
875 "metadata": {},
876 "source": [
877 "Programatic example of extending templates / cutom filters"
878 ]
879 },
880 {
881 "cell_type": "code",
882 "collapsed": false,
883 "input": [
884 "from IPython.nbconvert.filters.highlight import _pygment_highlight\n",
885 "from pygments.formatters import HtmlFormatter\n",
886 "\n",
887 "from IPython.nbconvert.exporters import HTMLExporter\n",
888 "from IPython.config import Config\n",
889 "\n",
890 "from IPython.nbformat import current as nbformat"
891 ],
892 "language": "python",
893 "metadata": {},
894 "outputs": [],
895 "prompt_number": 20
896 },
897 {
898 "cell_type": "markdown",
899 "metadata": {},
900 "source": [
901 "Here we define a dustom 'highlight' filter that apply a custom class to code in css. We register this filter with a already existing name, so it will replace the default one."
902 ]
903 },
904 {
905 "cell_type": "code",
906 "collapsed": false,
907 "input": [
908 "def my_highlight(source, language='ipython'):\n",
909 " formatter = HtmlFormatter(cssclass='highlight-ipynb')\n",
910 " return _pygment_highlight(source, formatter, language)\n",
911 " \n",
912 "c = Config({'CSSHtmlHeaderTransformer':\n",
913 " {'enabled':False, 'highlight_class':'highlight-ipynb'}})\n",
914 "\n",
915 "exportHtml = HTMLExporter( config=c , filters={'highlight2html': my_highlight} )\n",
916 "(body,resources) = exportHtml.from_notebook_node(jake_notebook)"
917 ],
918 "language": "python",
919 "metadata": {},
920 "outputs": [],
921 "prompt_number": 21
922 },
923 {
924 "cell_type": "code",
925 "collapsed": false,
926 "input": [
927 "i = body.index('highlight-ipynb')\n",
928 "body[i-12:i+50]"
929 ],
930 "language": "python",
931 "metadata": {},
932 "outputs": [
933 {
934 "metadata": {},
935 "output_type": "pyout",
936 "prompt_number": 22,
937 "text": [
938 "u'<div class=\"highlight-ipynb\"><pre><span class=\"kn\">from</span>'"
939 ]
940 }
941 ],
942 "prompt_number": 22
943 },
944 {
945 "cell_type": "heading",
946 "level": 3,
947 "metadata": {},
948 "source": [
949 "Programatically make templates"
950 ]
951 },
952 {
953 "cell_type": "code",
954 "collapsed": false,
955 "input": [
956 "from jinja2 import DictLoader\n",
957 "\n",
958 "dl = DictLoader({'html_full.tpl': \n",
959 "\"\"\"\n",
960 "{%- extends 'html_basic.tpl' -%} \n",
961 "\n",
962 "{% block footer %}\n",
963 "FOOOOOOOOTEEEEER\n",
964 "{% endblock footer %}\n",
965 "\"\"\"})\n",
966 "\n",
967 "\n",
968 "exportHtml = HTMLExporter( config=None , filters={'highlight': my_highlight}, extra_loaders=[dl] )\n",
969 "(body,resources) = exportHtml.from_notebook_node(jake_notebook)\n",
970 "for l in body.split('\\n')[-4:]:\n",
971 " print l"
972 ],
973 "language": "python",
974 "metadata": {},
975 "outputs": [
976 {
977 "output_type": "stream",
978 "stream": "stdout",
979 "text": [
980 "<p>This post was written entirely in an IPython Notebook: the notebook file is available for download <a href=\"http://jakevdp.github.com/downloads/notebooks/XKCD_plots.ipynb\">here</a>. For more information on blogging with notebooks in octopress, see my <a href=\"http://jakevdp.github.com/blog/2012/10/04/blogging-with-ipython/\">previous post</a> on the subject.</p>\n",
981 "</div>\n",
982 "FOOOOOOOOTEEEEER\n",
983 "\n"
984 ]
985 }
986 ],
987 "prompt_number": 23
988 },
989 {
990 "cell_type": "heading",
991 "level": 3,
992 "metadata": {},
993 "source": [
994 "Real World Use"
995 ]
996 },
997 {
998 "cell_type": "markdown",
999 "metadata": {},
1000 "source": [
1001 "@jakevdp use Pelican and IPython Notebook to blog. Pelican [Will use](https://github.com/getpelican/pelican-plugins/pull/21) nbconvert programatically to generate blog post. Have a look a [Pythonic Preambulations](http://jakevdp.github.io/) for Jake blog post."
1002 ]
1003 },
1004 {
1005 "cell_type": "markdown",
1006 "metadata": {},
1007 "source": [
1008 "@damianavila Wrote a Nicholas Plugin to [Write blog post as Notebook](http://www.damian.oquanta.info/posts/one-line-deployment-of-your-site-to-gh-pages.html) and is developping a js-extension to publish notebooks in one click from the web app."
1009 ]
1010 },
1011 {
1012 "cell_type": "markdown",
1013 "metadata": {},
1014 "source": [
1015 "<center>\n",
1016 "<blockquote class=\"twitter-tweet\"><p>As <a href=\"https://twitter.com/Mbussonn\">@Mbussonn</a> requested... easieeeeer! Deploy your Nikola site with just a click in the IPython notebook! <a href=\"http://t.co/860sJunZvj\">http://t.co/860sJunZvj</a> cc <a href=\"https://twitter.com/ralsina\">@ralsina</a></p>&mdash; Dami\u00e1n Avila (@damian_avila) <a href=\"https://twitter.com/damian_avila/statuses/370306057828335616\">August 21, 2013</a></blockquote>\n",
1017 "</center>"
1018 ]
1019 },
1020 {
1021 "cell_type": "markdown",
1022 "metadata": {},
1023 "source": [
1024 "And finaly, what you just did, is replicate what [nbviewer](http://nbviewer.ipython.org) does. WHich to fetch a notebook from url, convert it and send in back to you as a static html."
1025 ]
1026 },
1027 {
1028 "cell_type": "heading",
1029 "level": 5,
1030 "metadata": {},
1031 "source": [
1032 "A few gotchas"
1033 ]
1034 },
1035 {
1036 "cell_type": "markdown",
1037 "metadata": {},
1038 "source": [
1039 "Jinja blocks use `{% %}`by default which does not play nicely with $\\LaTeX$, hence thoses are replaced by `((* *))` in latex templates."
1040 ]
1041 },
1042 {
1043 "cell_type": "code",
1044 "collapsed": false,
1045 "input": [],
1046 "language": "python",
1047 "metadata": {},
1048 "outputs": []
1049 }
1050 ],
1051 "metadata": {}
1052 }
1053 ]
1054 } No newline at end of file
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -1,7 +1,7 b''
1 {
1 {
2 "metadata": {
2 "metadata": {
3 "name": "",
3 "name": "",
4 "signature": "sha256:33a14f36e4dad22b2fc23c59ce0e0ed96719007a09f616f240ed5f665e1b871f"
4 "signature": "sha256:a9f4b605a51f91e09f8e031235a0dd1b49a7056a2fecfec4b7f5bc6467f1e760"
5 },
5 },
6 "nbformat": 3,
6 "nbformat": 3,
7 "nbformat_minor": 0,
7 "nbformat_minor": 0,
@@ -58,7 +58,9 b''
58 "* [Basic Output](Basic Output.ipynb)\n",
58 "* [Basic Output](Basic Output.ipynb)\n",
59 "* [Working With Markdown Cells](Working With Markdown Cells.ipynb)\n",
59 "* [Working With Markdown Cells](Working With Markdown Cells.ipynb)\n",
60 "* [JavaScript Notebook Extensions](JavaScript Notebook Extensions.ipynb)\n",
60 "* [JavaScript Notebook Extensions](JavaScript Notebook Extensions.ipynb)\n",
61 "* [Notebook Security](Notebook Security.ipynb)"
61 "* [Notebook Security](Notebook Security.ipynb)\n",
62 "* [Converting Notebooks With nbconvert](Converting Notebooks With nbconvert.ipynb)\n",
63 "* [Using nbconvert as a Library](Using nbconvert as a Library.ipynb)"
62 ]
64 ]
63 },
65 },
64 {
66 {
1 NO CONTENT: file renamed from examples/Notebook/Working With Code Cells.ipynb to examples/Notebook/Running Code.ipynb
NO CONTENT: file renamed from examples/Notebook/Working With Code Cells.ipynb to examples/Notebook/Running Code.ipynb
General Comments 0
You need to be logged in to leave comments. Login now