Bidirectional IPython-Nikola workflow to write your blog post

  |   Source   |   Minimap

The idea of this blog post is to show you how you can achieve an efficient bidirectional workflow to write your blog posts using great tools/features derived from IPython and Nikola.

Probably, this would be interesting not only for the people using IPython and Nikola to write their blog post, but also to other people because this would be a simple but nice example of how you can integrate this two applications to better suit your needs.

OK, first of all, let's go to the folder containing my blog:

damian@damian-S400CA:~$ cd /media/datos/Desarrollos/damian_blog

damian@damian-S400CA:/media/datos/Desarrollos/damian_blog$

Then I activate my virtualenv specifically filled to build my blog:

damian@damian-S400CA:/media/datos/Desarrollos/damian_blog$ workon blog

(blog)damian@damian-S400CA:/media/datos/Desarrollos/damian_blog$ 

Now, we are ready to the next step, calling the nikola console:

(blog)damian@damian-S400CA:/media/datos/Desarrollos/damian_blog$ nikola console
Scanning posts......done!
Python 2.7.4 (default, Apr 19 2013, 18:28:01) 
Type "copyright", "credits" or "license" for more information.

IPython 1.1.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.


Nikola v6.1.1 -- IPython Console (conf = configuration, SITE = site engine)

Yes... Nikola offers a feature called nikola console which immediately opens an IPython console (if you have installed IPython, of course) where you can do a lot of things, as I will show you later, but also have direct access to Nikola internals through the conf (configuration) and SITE (site engine) objects. We can explore this objects using the tab completion from the IPython machinery:

In [1]: conf.<TAB>
conf.BLOG_AUTHOR             conf.LICENSE
conf.BLOG_DESCRIPTION        conf.MATHJAX_CONFIG
conf.BLOG_EMAIL              conf.NAVIGATION_LINKS
conf.BLOG_TITLE              conf.PAGES
conf.BODY_END                conf.POSTS
conf.COMMENT_SYSTEM_ID       conf.py
conf.COMPILERS               conf.pyc
conf.CONTENT_FOOTER          conf.SITE_URL
conf.CREATE_MONTHLY_ARCHIVE  conf.SOCIAL_BUTTONS_CODE
conf.DEFAULT_LANG            conf.THEME
conf.DEPLOY_COMMANDS         conf.time
conf.GLOBAL_CONTEXT          conf.TRANSLATIONS
conf.INDEX_TEASERS           conf.unicode_literals
conf.IPYNB_CONFIG 
In [1]: SITE.<TAB>
SITE.abs_link                    SITE.MESSAGES
SITE.clean_task_paths            SITE.pages
SITE.commands                    SITE.path
SITE.compilers                   SITE.plugin_manager
SITE.config                      SITE.post_per_file
SITE.configured                  SITE.posts_per_category
SITE.current_lang                SITE.posts_per_month
SITE.default_lang                SITE.posts_per_tag
SITE.EXTRA_PLUGINS               SITE.posts_per_year
SITE.file_exists                 SITE.rel_link
SITE.generic_page_renderer       SITE.render_template
SITE.generic_post_list_renderer  SITE.scan_posts
SITE.gen_tasks                   SITE.strict
SITE.get_compiler                SITE.template_system
SITE.GLOBAL_CONTEXT              SITE.THEMES
SITE.global_data                 SITE.timeline
SITE.inverse_compilers           SITE.translations
SITE.link 

But, we will probably go back to these objects in another blog post... now, we go again to the Nikola IPython console because we want to create a new post. The easiest way to do it is just calling the proper Nikola command using the ! to pass it from the Nikola IPython console to the shell.

In [1]: !nikola new_post -f ipynb

But we can do it better ;-) We can pass arguments to the nikola new_post command such as the title, tags, format (we have already pass the format with -f ipynb), etc. So, let me create some variables for these arguments inside my Nikola IPython console session:

In [1]: title = "Bidirectional IPython-Nikola workflow to write your blog post"

In [2]: tags_list = ['python', 'IPython', 'nikola', 'blog', 'extension', 'gh-pages', 'git', 'workflow']

In [3]: tags = ', '.join(tags_list)

In [4]: tags
Out[4]: u'python, IPython, nikola, blog, extension, gh-pages, git, workflow'

Here I have the string title containing the title for the current blog post and a list called tags_list containing the corresponding tags. Later I call the .join method because I will need to pass a string with the tags sepatarated by commas to the nikola new_post command.

But, wait a minute... I have Python variables containing the title and the tags for my blog post. How can pass this variables to the shell to use them as arguments of the nikola new_post command???

OK, to solve this problem we will use an exciting feature from IPython, just see the following line:

In [5]: !nikola new_post -f ipynb -t "{title}" --tags="{tags}"

which will create the desired ipynb and the corresponding .meta file containing the title and tags.

Creating New Post
-----------------

Title: Bidirectional IPython-Nikola workflow to write your blog post
Scanning posts......done!
Your post's metadata is at:  posts/bidirectional-ipython-nikola-workflow-to-write-your-blog-post.meta
[2013-10-15T19:21:28Z] NOTICE: new_post: Your post's text is at: posts/bidirectional-ipython-nikola-workflow-to-write-your-blog-post.ipynb

Que me contusi! (or... do you see the bidirectional nature of this interaction? Nice, don't you think?)

Yes, inside IPython, we can use the braces to pass Python variables to the shell... ;-)

Do you see the possibilities we have with this little feature?

Now, I cd into the post folder of my site and open the IPython notebook with my custom extensions.

In [6]: cd posts/
/media/datos/Desarrollos/damian_blog/posts

In [7]: !ipython notebook --profile=myext

In the IPython Dashboard, I will open the new ipynb called bidirectional-ipython-nikola-workflow-to-write-your-blog-post.ipynb and write the content (I am writing right now).

Then, when I am done with the blog post, I need to deploy my new content to somewhere to make available to the public...

And this is very easy...

First, Nikola have a nikola deploy command which you can customize with your conf.py file. You can see it here my configuration:

In [8]:
!sed -n 197,208p conf.py
  Click me to hide the output
# Commands to execute to deploy. Can be anything, for example,
# you may use rsync:
# "rsync -rav output/* joe@my.site:/srv/www/site"
# And then do a backup, or ping pingomatic.
# To do manual deployment, set it to []
DEPLOY_COMMANDS = ["git add .",
                   "git commit -am 'Update'",
                   "git push origin master",
                   "git subtree split --prefix output -b gh-pages",
                   "git push -f origin gh-pages:gh-pages",
                   "git branch -D gh-pages"]

The instructions are very easy to understand, if you need more details just read this post I wrote some weeks ago.

I am essentially pushing the content to master, creating a split and deploy it later to gh-pages.

OK, but because I am lazy ;-), some weeks ago I wrote an IPython javascript extension which let me execute this nikola deploy command from a button in the IPython notebook toolbar. Essentially something like this (don't worry, I will release the extension soon):

function nikolaDeploy(path, clean) {
  IPython.notebook.kernel.execute('cd ' + path);
  if (clean=="True") {
    IPython.notebook.kernel.execute('!nikola clean');
  }else{
  //do nothing
  }
  IPython.notebook.kernel.execute('!nikola build');
  IPython.notebook.kernel.execute('!nikola deploy');
  messager();
}

As you can see, I call nikola clean to clean my site, the I built it with nikola build, and deploy it with my customized nikola deploy... and all these actions with just a click!

And now you have the complete workflow! A very simple one, because you can make a lot of modifications to improve it. However, this workflow show you how you can integrate two exciting projects, communicate one with the other and viceversa, and get beautiful results.

Hope you enjoy it!

Damián.

Did you like the content? Great!

Or visit my support page for more information.


Btw, don't forget this blog post is an ipynb file itself! So, you can download it from the "Source" link at the top of the post if you want to play with it ;-)

Comments powered by Disqus