The power of simple python scripting is one of the themes of this blog, and now I can show a great practical example.
I needed to create something that looks like this:
This is a simple HTML table with some CSS. But trying to write it in HTML would result in a lot of copy-pasting of stuff that looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<td colspan="2" rowspan="2"> | |
<h3>Value Propositions</h3> | |
<div class="postit "><i>Awesome</i> AR Geek Shop</div> | |
<div class="postit ">Infinite shelf space for long-tail products</div> | |
<div class="postit cl ">Products connected to info online</div> | |
<div class="postit ">Products connected to geeks in other stores</div> | |
<div class="postit cl ">Sense of community</div> | |
<div class="postit ">Pleasure of random surprises</div> | |
<div class="postit cl ">Easily to setup mobile shop</div> | |
<div class="postit ">Platform for interesting promotions</div> | |
</td> |
That’s a lot of boring and error prone copy-pasting. And I’ll need to create lots of these in the coming months.
OK, so I need to write a script that will turn this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CANVAS = { | |
"customers": [x + " Fans" for x in ["Comics", "RPG", " MTG", "Anime"]] + [" Gamers"], | |
"values": [ | |
"<i>Awesome</i> AR Geek Shop", | |
"Infinite shelf space for long-tail products", | |
" Products connected to info online", | |
"Products connected to geeks in other stores", | |
" Sense of community", | |
"Pleasure of random surprises", | |
" Easily to setup mobile shop", | |
"Platform for interesting promotions", | |
], | |
"channels": ["Conventions as marketing", "Retail (our shops)", " Online", "Geek events we host"], | |
"relationships": ["Physical", "Online Community", " Promotions", "Geek Events"], | |
"revenues": ["Asset sales"], | |
"resources": ["Shops", "Registered users", " Merchandise", "Promotions"], | |
"activities": ["Sales", "Marketing", " Hosting events"], | |
"partners": [ | |
"WotC?", | |
" Marvel?", | |
" DC?", | |
" TOKYOPOP ?" | |
], | |
"costs": ["One time: Development", "Constant: Rent", "Initial Merchandise", "Employee salary"], | |
} |
Into the correct HTML.
It’s actually really easy:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
HTML = """<?doctype html> | |
<html> | |
<head> | |
<style> | |
body { | |
background-color: gray; | |
} | |
// … | |
.cl { | |
clear: left; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>The Business Model Canvas</h1> | |
<h2>Designed for: %%s</h2> | |
<h2>Designed by: Aur Saraf</h2> | |
<table> | |
<tr> | |
<td rowspan="2"> | |
<h3>Key Partners</h3> | |
%(partners)s | |
</td> | |
<td> | |
<h3>Key Activities</h3> | |
%(activities)s | |
</td> | |
<td colspan="2" rowspan="2"> | |
<h3>Value Propositions</h3> | |
%(values)s | |
</td> | |
</tr> | |
<!– … –!> | |
</table> | |
<!– … –!> | |
</body> | |
</html> | |
""" | |
POSTIT = '<div class="postit %(classes)s">%(content)s</div>' | |
CANVAS_CODE = """CANVAS = { | |
"customers": [], | |
"values": [], | |
"channels": [], | |
"relationships": [], | |
"revenues": [], | |
"resources": [], | |
"activities": [], | |
"partners": [], | |
"costs": [], | |
} | |
""" | |
def to_postit(caption): | |
classes = "" | |
if caption.startswith(" "): | |
classes = "cl " | |
caption = caption[1:] | |
return POSTIT % {"classes": classes, "content": caption} | |
def write(name, canvas, f): | |
canvas = dict((k, "\n".join(map(to_postit, v))) for k, v in canvas.iteritems()) | |
html = HTML % canvas % name | |
f.write(html) | |
def main(): | |
if len(sys.argv) == 3 and sys.argv[1] == "-new": | |
with file(sys.argv[2] + ".py", "wb") as f: | |
f.write(CANVAS_CODE) | |
return 0 | |
elif len(sys.argv) != 2: | |
print "usage: canvas.py name" | |
return 1 | |
name = sys.argv[1] | |
module = __import__(name) | |
with file(name + ".html", "wb") as f: | |
write(name, module.CANVAS, f) | |
return 0 | |
if __name__ == '__main__': | |
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ python canvas.py -new geekar | |
$ emacs geekar.py | |
$ python canvas.py geekar |
It’s a small command-line tool that knows how to create an empty .py file for a canvas with the correct template, and knows how to read a .py file as a python file (since this is for self-use only I’m fine with no security at all, otherwise reading it as a python file would be very unwise) and do some preprocessing and some search-and-replacin’ to get the right thing into an HTML template.
I needed to be able to control how many notes to have in a row, so I added parsing of a space at the beginning of a note caption to mean “newline before this”. I needed to write many similar notes for “customer segments” – so I used the fact that the canvas definition file is in python to just write some python that generates it:
“customers”: [x + ” Fans” for x in [“Comics”, “RPG”, ” MTG”, “Anime”]] + [” Gamers”],
And that’s all there is to it. One of my students could write this in ten minutes. From now on, something that I will be doing every day will take a few minutes less.
The whole code can be found here: https://github.com/SonOfLilit/quickbmc and the complete result here: https://github.com/SonOfLilit/quickbmc/tree/gh-pages.