tag:blogger.com,1999:blog-7300609595378108412024-03-05T00:56:05.844-08:00Code Makes Me HappyPython, JS, CSS, HTML5, open source, hacking, funUnknownnoreply@blogger.comBlogger20125tag:blogger.com,1999:blog-730060959537810841.post-63727808308114408562019-09-18T17:46:00.001-07:002022-11-26T06:35:52.320-08:00Voronoi MandalasSciPy has tools for creating Voronoi tessellations. Besides the obvious data science applications, you can use them to make pretty art like this:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTWxLgqNOFnpEUGMb8mPM7gdECW9Y1PVsMzw9jYAUKBJgbp3819UlXYSzl09vdPOl8tI_DEcwnKog11AAzlkBOHEpUnMw_WktOHZxEeUybL8GQhGZ-CtJUVSLhH7l4JexTZQbTtULroBY/s1600/mandala02.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1000" data-original-width="1000" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTWxLgqNOFnpEUGMb8mPM7gdECW9Y1PVsMzw9jYAUKBJgbp3819UlXYSzl09vdPOl8tI_DEcwnKog11AAzlkBOHEpUnMw_WktOHZxEeUybL8GQhGZ-CtJUVSLhH7l4JexTZQbTtULroBY/s640/mandala02.png" width="640" /></a></div>
The above was generated by this code:<br />
<br />
<br />
<script src="https://gist.github.com/audreyfeldroy/0c5d0923501c08a7cb7b13e08c66a7ad.js"></script>
<br />
<br />
I started with Carlos Focil's <a href="https://github.com/CarlosFocil/mandalapy">mandalapy</a> code, modifying the parameters until I had a design I liked. I decided to make the Voronoi diagram show both points and vertices, and I gave it an equal aspect ratio. Carlos' mandalapy code is a port of <a href="https://fronkonstin.com/">Antonio Sánchez Chinchón</a>'s inspiring work <a href="https://fronkonstin.com/2018/02/14/mandalas/">drawing mandalas with R</a>, using the <a href="https://cran.r-project.org/web/packages/deldir/index.html">deldir</a> library to plot Voronoi tesselations.Audrey Roy Greenfeldhttp://www.blogger.com/profile/02453284738449712151noreply@blogger.com5Los Angeles, CA, USA34.0522342 -118.243684933.2099567 -119.5345784 34.8945117 -116.95279140000001tag:blogger.com,1999:blog-730060959537810841.post-58461178169524130932016-04-28T21:32:00.002-07:002016-04-28T23:53:03.662-07:00Lazy Evaluation and SQL Queries in the Django ShellIn Django terms, a QuerySet is an iterable of database records. What's nice about them is that they are evaluated only when you're ready for the results.<br />
<br />
This means that even if it takes you a few lines of code to chain multiple queries, the Django ORM combines them into a single query. Less queries mean your database doesn't have to work as hard, and your website runs faster.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRTm7SNn9yAHD6yJHp7kuHdm6ibL6T7QXxXiLrINO0g3DYo0jRqIi03UqGz5UZwsXYWbNSH_8qIoygodip6VOdy4wepMxfUjginUhWsJHG42Yg7P9MKNDLEJjGNeCpIieKzfFUDjROsgg/s1600/django-tips-600x314.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRTm7SNn9yAHD6yJHp7kuHdm6ibL6T7QXxXiLrINO0g3DYo0jRqIi03UqGz5UZwsXYWbNSH_8qIoygodip6VOdy4wepMxfUjginUhWsJHG42Yg7P9MKNDLEJjGNeCpIieKzfFUDjROsgg/s1600/django-tips-600x314.png" /></a></div>
<br />
<h2>
Evaluating a QuerySet Repeatedly</h2>
Imagine that we work for Häagen-Dazs and have access to their Django shell. We can use this to our advantage by hunting for free ice cream promotions.<br />
<br />
Here, we get the active Promo objects. We evaluate the results just to see what promos are available. Then we filter them on the word free.<br />
<br />
<div style="background: #272822; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">results</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">Promo</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">objects</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">active()</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">results</span>
<span style="color: #f8f8f2;">[</span><span style="color: #f92672;"><</span><span style="color: #f8f8f2;">Promo:</span> <span style="color: #f8f8f2;">Free</span> <span style="color: #f8f8f2;">Flavors</span> <span style="color: #f8f8f2;">on</span> <span style="color: #f8f8f2;">Your</span> <span style="color: #f8f8f2;">Birthday</span><span style="color: #f92672;">></span><span style="color: #f8f8f2;">,</span> <span style="color: #f92672;"><</span><span style="color: #f8f8f2;">Promo:</span> <span style="color: #ae81ff;">10</span><span style="color: #f92672;">%</span> <span style="color: #f8f8f2;">Off</span> <span style="color: #f8f8f2;">All</span> <span style="color: #f8f8f2;">Cones</span><span style="color: #f92672;">></span><span style="color: #f8f8f2;">,</span>
<span style="color: #f92672;"><</span><span style="color: #f8f8f2;">Promo:</span> <span style="color: #f8f8f2;">Buy</span> <span style="color: #ae81ff;">1</span><span style="color: #f8f8f2;">,</span> <span style="color: #f8f8f2;">Get</span> <span style="color: #ae81ff;">1</span> <span style="color: #f8f8f2;">Free</span><span style="color: #f92672;">></span><span style="color: #f8f8f2;">]</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">results</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">results</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">filter(</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">Q(name__istartswith</span><span style="color: #f92672;">=</span><span style="color: #e6db74;">'free'</span><span style="color: #f8f8f2;">)</span> <span style="color: #f92672;">|</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">Q(description__icontains</span><span style="color: #f92672;">=</span><span style="color: #e6db74;">'free'</span><span style="color: #f8f8f2;">)</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">)</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">results</span>
<span style="color: #f8f8f2;">[</span><span style="color: #f92672;"><</span><span style="color: #f8f8f2;">Promo:</span> <span style="color: #f8f8f2;">Free</span> <span style="color: #f8f8f2;">Flavors</span> <span style="color: #f8f8f2;">on</span> <span style="color: #f8f8f2;">Your</span> <span style="color: #f8f8f2;">Birthday</span><span style="color: #f92672;">></span><span style="color: #f8f8f2;">]</span>
</pre>
</div>
<br />
The queries generated by the above are:<br />
<br />
<div style="background: #272822; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #f92672;">from</span> <span style="color: #f8f8f2;">django.db</span> <span style="color: #f92672;">import</span> <span style="color: #f8f8f2;">connection</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">connection</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">queries</span>
<span style="color: #f8f8f2;">[</span> <span style="color: #f8f8f2;">{</span><span style="color: #e6db74;">'sql'</span><span style="color: #f8f8f2;">:</span> <span style="color: #e6db74;">'SELECT "flavors_promo"."id", "flavors_promo"."name", </span>
<span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"description"</span><span style="color: #f8f8f2;">,</span> <span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"status"</span> <span style="color: #f8f8f2;">FROM</span>
<span style="color: #e6db74;">"flavors_promo"</span> <span style="color: #f8f8f2;">WHERE</span> <span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"status"</span> <span style="color: #f92672;">=</span> \<span style="color: #e6db74;">'active</span><span style="color: #ae81ff;">\'</span><span style="color: #e6db74;"> </span>
<span style="color: #f8f8f2;">LIMIT</span> <span style="color: #ae81ff;">21</span><span style="color: #e6db74;">',</span>
<span style="color: #e6db74;">'time'</span><span style="color: #f8f8f2;">:</span> <span style="color: #e6db74;">'0.000'</span><span style="color: #f8f8f2;">},</span>
<span style="color: #f8f8f2;">{</span><span style="color: #e6db74;">'sql'</span><span style="color: #f8f8f2;">:</span> <span style="color: #e6db74;">'SELECT "flavors_promo"."id", "flavors_promo"."name", </span>
<span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"description"</span><span style="color: #f8f8f2;">,</span> <span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"status"</span> <span style="color: #f8f8f2;">FROM</span>
<span style="color: #e6db74;">"flavors_promo"</span> <span style="color: #f8f8f2;">WHERE</span> <span style="color: #f8f8f2;">(</span><span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"status"</span> <span style="color: #f92672;">=</span> \<span style="color: #e6db74;">'active</span><span style="color: #ae81ff;">\'</span><span style="color: #e6db74;"> </span>
<span style="color: #f8f8f2;">AND</span> <span style="color: #f8f8f2;">(</span><span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"name"</span> <span style="color: #f8f8f2;">LIKE</span> \<span style="color: #e6db74;">'free%</span><span style="color: #ae81ff;">\'</span><span style="color: #e6db74;"> ESCAPE </span><span style="color: #ae81ff;">\'\\\'</span><span style="color: #e6db74;"> OR </span>
<span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"description"</span> <span style="color: #f8f8f2;">LIKE</span> \<span style="color: #e6db74;">'%free%</span><span style="color: #ae81ff;">\'</span><span style="color: #e6db74;"> ESCAPE </span><span style="color: #ae81ff;">\'\\\'</span><span style="color: #e6db74;">)) </span>
<span style="color: #f8f8f2;">LIMIT</span> <span style="color: #ae81ff;">21</span><span style="color: #e6db74;">',</span>
<span style="color: #e6db74;">'time'</span><span style="color: #f8f8f2;">:</span> <span style="color: #e6db74;">'0.001'</span><span style="color: #f8f8f2;">}]</span>
</pre>
</div>
<br />
There are 2 queries because we evaluated the results twice.<br />
<br />
The first query was from the first time we retrieved all the active promos. It's pretty short. It just selects Promo records where promo.status is active.<br />
<br />
The second query was from the second time we evaluated results, after we filtered for "free" in the promo names and descriptions.<br />
<br />
As a side note, there is a bit of extra work in the second query as the second query still has that WHERE 'flavors_promo'.'status' = 'active' part. One might expect filter() to simply filter on the already-retrieved results rather than hitting the database again. But that's alright because the extra time is negligible.<br />
<br />
Before we move on, let's reset the list of queries:<br />
<br />
<div style="background: #272822; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #f92672;">>>></span> <span style="color: #f92672;">from</span> <span style="color: #f8f8f2;">django.db</span> <span style="color: #f92672;">import</span> <span style="color: #f8f8f2;">reset_queries</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">reset_queries()</span>
</pre>
</div>
<h2>
Evaluating a QuerySet Once</h2>
Now, let's look at what the queries would be if we only evaluated the results QuerySet once. Let's try building the same QuerySet again. Oh wait, just for fun, let's chain another operation so that we can be really sure that lazy evaluation is happening.<br />
<br />
<div style="background: #272822; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">results</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">Promo</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">objects</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">active()</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">results</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">results</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">filter(</span>
<span style="color: #f92672;">...</span> <span style="color: #f8f8f2;">Q(name__istartswith</span><span style="color: #f92672;">=</span><span style="color: #f8f8f2;">name)</span> <span style="color: #f92672;">|</span>
<span style="color: #f92672;">...</span> <span style="color: #f8f8f2;">Q(description__icontains</span><span style="color: #f92672;">=</span><span style="color: #f8f8f2;">name)</span>
<span style="color: #f92672;">...</span> <span style="color: #f8f8f2;">)</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">results</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">results</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">exclude(status</span><span style="color: #f92672;">=</span><span style="color: #e6db74;">'melted'</span><span style="color: #f8f8f2;">)</span>
<span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">results</span>
<span style="color: #f8f8f2;">[</span><span style="color: #f92672;"><</span><span style="color: #f8f8f2;">Promo:</span> <span style="color: #f8f8f2;">Free</span> <span style="color: #f8f8f2;">Flavors</span> <span style="color: #f8f8f2;">on</span> <span style="color: #f8f8f2;">Your</span> <span style="color: #f8f8f2;">Birthday</span><span style="color: #f92672;">></span><span style="color: #f8f8f2;">]</span>
</pre>
</div>
<br />
As you can see, there's only one query:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #272822; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #f92672;">>>></span> <span style="color: #f8f8f2;">connection</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">queries</span>
<span style="color: #f8f8f2;">[{</span><span style="color: #e6db74;">'sql'</span><span style="color: #f8f8f2;">:</span> <span style="color: #e6db74;">'SELECT "flavors_promo"."id", "flavors_promo"."name", </span>
<span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"description"</span><span style="color: #f8f8f2;">,</span> <span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"status"</span> <span style="color: #f8f8f2;">FROM</span>
<span style="color: #e6db74;">"flavors_promo"</span> <span style="color: #f8f8f2;">WHERE</span> <span style="color: #f8f8f2;">(</span><span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"status"</span> <span style="color: #f92672;">=</span> \<span style="color: #e6db74;">'active</span><span style="color: #ae81ff;">\'</span><span style="color: #e6db74;"> AND</span>
<span style="color: #f8f8f2;">(</span><span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"name"</span> <span style="color: #f8f8f2;">LIKE</span> \<span style="color: #e6db74;">'free%</span><span style="color: #ae81ff;">\'</span><span style="color: #e6db74;"> ESCAPE </span><span style="color: #ae81ff;">\'\\\'</span><span style="color: #e6db74;"> OR </span>
<span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"description"</span> <span style="color: #f8f8f2;">LIKE</span> \<span style="color: #e6db74;">'%free%</span><span style="color: #ae81ff;">\'</span><span style="color: #e6db74;"> ESCAPE </span><span style="color: #ae81ff;">\'\\\'</span><span style="color: #e6db74;">) AND </span>
<span style="color: #f8f8f2;">NOT</span> <span style="color: #f8f8f2;">(</span><span style="color: #e6db74;">"flavors_promo"</span><span style="color: #f92672;">.</span><span style="color: #e6db74;">"status"</span> <span style="color: #f92672;">=</span> \<span style="color: #e6db74;">'melted</span><span style="color: #ae81ff;">\'</span><span style="color: #e6db74;">)) LIMIT 21'</span><span style="color: #f8f8f2;">,</span>
<span style="color: #e6db74;">'time'</span><span style="color: #f8f8f2;">:</span> <span style="color: #e6db74;">'0.001'</span><span style="color: #f8f8f2;">}]</span>
</pre>
</div>
<br />
Thanks to lazy evaluation, only one query was constructed, despite chaining multiple operations. That was nice.<br />
<br />
Sure, the query could have been more optimal without the AND NOT melted part, but arguably that wasn't Django's fault, it was mine. But it gives me a clue about which operation I didn't need to chain in the Python code.<br />
<h2>
Next Steps</h2>
Try this on one of your projects. Open the Django shell, then try out some queries and see how they are evaluated. In particular, look at queries from one of your slower views.<br />
<br />
You can also do similar things with Django Debug Toolbar. However, in the shell you can dissect your Python code line by line, which can be very helpful.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-73936685986152163152015-11-09T08:16:00.002-08:002015-11-09T08:16:53.479-08:00Solving UnicodeDecodeErrors Due to Opening Binary Files<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2joIwQ0V0d_5VzQkXwlWLsUlAZv50OFzoTKtH-TkhVXqM6SCjIBrKoiJmK43u0d_V8Ow5ufGxESxUvoCbbnrJiPZEU2kyMIeyB9-He5lcL7AiCJyWm4-1jx_ks8Zz2ZeLunW7Sk5cX2I/s1600/python-tip-600x314.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2joIwQ0V0d_5VzQkXwlWLsUlAZv50OFzoTKtH-TkhVXqM6SCjIBrKoiJmK43u0d_V8Ow5ufGxESxUvoCbbnrJiPZEU2kyMIeyB9-He5lcL7AiCJyWm4-1jx_ks8Zz2ZeLunW7Sk5cX2I/s1600/python-tip-600x314.png" /></a></div>
<br /><span id="goog_224512819"></span>
<h2>
Common Scenario: Walking Directory Tree and Opening Files</h2>
A common thing to do in Python is to go through a directory tree, opening each file and doing something with the file's text.<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #272822; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #66d9ef;">for</span> <span style="color: #f8f8f2;">path</span> <span style="color: #f92672;">in</span> <span style="color: #f8f8f2;">paths:</span>
<span style="color: #66d9ef;">for</span> <span style="color: #f8f8f2;">line</span> <span style="color: #f92672;">in</span> <span style="color: #f8f8f2;">open(path,</span> <span style="color: #e6db74;">'r'</span><span style="color: #f8f8f2;">):</span>
<span style="color: #75715e;"># Do something with each line of the file here.</span>
<span style="color: #75715e;"># Go ahead, right inside the for loop.</span>
<span style="color: #75715e;"># It's a text file, so imagine the possibilities.</span>
</pre>
</div>
<br />
Here, we iterate over all the paths in the directory tree. For each path, we open the file for reading. Then we go through each line of the file and do something with it.<br />
<br />
<h2>
The Problem</h2>
This works well enough for many situations, but at some point you end up running into a UnicodeDecodeError when you try to open a particular file. Usually, it's because that file isn't a text file: for example, it might be a JPEG or a font file.<br />
<br />
Those errors are scary! They look like this:
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #272822; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #66d9ef;">for</span> <span style="color: #f8f8f2;">line</span> <span style="color: #f92672;">in</span> <span style="color: #f8f8f2;">open(path,</span> <span style="color: #e6db74;">'r'</span><span style="color: #f8f8f2;">):</span>
<span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span> <span style="color: #f8f8f2;">_</span>
<span style="color: #f8f8f2;">self</span> <span style="color: #f92672;">=</span> <span style="color: #f92672;"><</span><span style="color: #f8f8f2;">encodings</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">utf_8</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">IncrementalDecoder</span> <span style="color: #f8f8f2;">object</span> <span style="color: #f8f8f2;">at</span> <span style="color: #ae81ff;">0x10349a320</span><span style="color: #f92672;">></span>
<span style="color: #f8f8f2;">input</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">b</span><span style="color: #e6db74;">"</span><span style="color: #ae81ff;">\x00\x00\x01\x00\x02\x00</span><span style="color: #e6db74;"> </span><span style="color: #ae81ff;">\x00\x00\x01\x00</span><span style="color: #e6db74;"> </span><span style="color: #ae81ff;">\x00</span><span style="color: #e6db74;">(</span><span style="color: #ae81ff;">\x10\x00\x00</span><span style="color: #e6db74;">&</span><span style="color: #ae81ff;">\x00\x00\x00\x10\x10\x00\x00\x01\x00</span><span style="color: #e6db74;"> </span><span style="color: #ae81ff;">\x00</span><span style="color: #e6db74;">(</span><span style="color: #ae81ff;">\x04\x00\x00</span><span style="color: #e6db74;">N...00</span><span style="color: #ae81ff;">\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00</span><span style="color: #e6db74;">"</span>
<span style="color: #f8f8f2;">final</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">False</span>
<span style="color: #66d9ef;">def</span> <span style="color: #a6e22e;">decode</span><span style="color: #f8f8f2;">(self,</span> <span style="color: #f8f8f2;">input,</span> <span style="color: #f8f8f2;">final</span><span style="color: #f92672;">=</span><span style="color: #f8f8f2;">False):</span>
<span style="color: #75715e;"># decode input (taking the buffer into account)</span>
<span style="color: #f8f8f2;">data</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">self</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">buffer</span> <span style="color: #f92672;">+</span> <span style="color: #f8f8f2;">input</span>
<span style="color: #f92672;">></span> <span style="color: #f8f8f2;">(result,</span> <span style="color: #f8f8f2;">consumed)</span> <span style="color: #f92672;">=</span> <span style="color: #f8f8f2;">self</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">_buffer_decode(data,</span> <span style="color: #f8f8f2;">self</span><span style="color: #f92672;">.</span><span style="color: #f8f8f2;">errors,</span> <span style="color: #f8f8f2;">final)</span>
<span style="color: #f8f8f2;">E</span> <span style="color: #a6e22e;">UnicodeDecodeError</span><span style="color: #f8f8f2;">:</span> <span style="color: #e6db74;">'utf-8'</span> <span style="color: #f8f8f2;">codec</span> <span style="color: #f8f8f2;">can</span><span style="color: #e6db74;">'t decode byte 0xad in position 89: invalid start byte</span>
</pre>
</div>
<br />
Before you go into a UnicodeDecodePanic trying out all the variants of open, io.open, unicode_open, etc., think about whether the file you're trying to open is even a text file.<br />
<br />
<h2>
The Solution</h2>
To solve the problem of accidentally opening non-text files, you can use <a href="https://github.com/audreyr/binaryornot">BinaryOrNot</a>'s is_binary function. Just check to make sure the file isn't a binary before attempting to open it, like this:
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #272822; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #f92672;">from</span> <span style="color: #f8f8f2;">binaryornot.check</span> <span style="color: #f92672;">import</span> <span style="color: #f8f8f2;">is_binary</span>
<span style="color: #66d9ef;">for</span> <span style="color: #f8f8f2;">path</span> <span style="color: #f92672;">in</span> <span style="color: #f8f8f2;">paths:</span>
<span style="color: #66d9ef;">if</span> <span style="color: #f92672;">not</span> <span style="color: #f8f8f2;">is_binary(path):</span>
<span style="color: #66d9ef;">for</span> <span style="color: #f8f8f2;">line</span> <span style="color: #f92672;">in</span> <span style="color: #f8f8f2;">open(path,</span> <span style="color: #e6db74;">'r'</span><span style="color: #f8f8f2;">):</span>
<span style="color: #75715e;"># Do something with each line of the file here.</span>
<span style="color: #75715e;"># Go ahead, right inside the for loop.</span>
<span style="color: #75715e;"># It's a text file, so imagine the possibilities.</span>
</pre>
</div>
<br />
This is a real-life code example. In fact, it comes from a fix to cookiecutter-django's tests that I just <a href="https://github.com/pydanny/cookiecutter-django/commit/7e8f58d6ec472f6e5effc291f00471d32d64c686">committed this weekend</a>, which comes from <a href="https://github.com/audreyr/cookiecutter/blob/5b997e8e2f5d4773eff62f95afe7426c23bbfeed/cookiecutter/generate.py#L156-L159">Cookiecutter core code</a>.<br />
<br />
BinaryOrNot is a package that guesses whether a file is binary or text. I put it together a couple of years ago in order to use it in Cookiecutter. Since then, I've found uses for it over and over in various projects.<br />
<br />
<h2>
More Info</h2>
BinaryOrNot on GitHub: <a href="https://github.com/audreyr/binaryornot">https://github.com/audreyr/binaryornot</a><br />
Project documentation: <a href="http://binaryornot.readthedocs.org/">http://binaryornot.readthedocs.org/</a>Audrey Roy Greenfeldhttp://www.blogger.com/profile/02453284738449712151noreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-48304590208844312292015-11-03T09:12:00.000-08:002015-11-09T08:21:45.541-08:00Intensive Django Training With the 91st Cyberspace Operations SquadronDaniel and I just returned from a trip to San Antonio, Texas, where we taught one of our intensive Django training workshops at Lackland Air Force Base.<br />
<br />
We prepared a customized version of our curriculum to meet the needs of the <a href="https://en.wikipedia.org/wiki/91st_Cyberspace_Operations_Squadron">91st Cyberspace Operations Squadron</a> of the US Air Force.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.codemakesmehappy.com/2015/11/intensive-django-training-with-91st.html" imageanchor="1" style="margin-bottom: 1em; margin-right: 1em; text-align: left;"><img alt="Our intensive Django training at Lackland Air Force Base in San Antonio, Texas." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEP5gBnWSkX3dqhqzZU2xOU1t4WvC-jmvwO7WizTOc50VrQyLBp7zMbCgb5Rh_O7Ve9-jSWaZH3dj8kmideLaOKiVf37IoswofEqC9XeB9-yctMVe25kkryxAqdq1avwR8n7oQO24AAPE/s1600/two-scoops-academy-91cos-django-training.png" title="Intensive Django Training with Two Scoops Academy and the 91st Cyberspace Operations Squadron, Lackland Air Force Base, San Antonio, Texas" /></a></div>
<br />
Teaching such a sharp, enthusiastic group and seeing everyone grasp difficult concepts so rapidly was a huge thrill. As instructors who like challenges, we tend to err on the side of assuming that our students can handle anything, so we threw a lot of very advanced topics at the group, wondering how much would click. On the last day as they were putting their knowledge into practice during hands-on project time, it was apparent that even the hardest parts had made an imprint.<br />
<br />
For more info, see <a href="http://www.pydanny.com/intensive-django-training-us-air-force.html">Intensive Django Training with the US Air Force</a>, Daniel's detailed blog post about the training experience.<br />
<br />
Special thanks to <a href="https://twitter.com/jondelmil">Capt. Jonathan D. Miller</a> for making this possible. It was an honor to work with you and your team.Unknownnoreply@blogger.com0Lackland AFB, San Antonio, TX, USA29.3877765 -98.62051930000001229.332434000000003 -98.701200300000011 29.443119 -98.539838300000014tag:blogger.com,1999:blog-730060959537810841.post-63864697812858789322015-05-26T10:23:00.000-07:002015-05-27T11:08:55.511-07:00Our Trip to DjangoGirls Ensenada, Mexico<p>This weekend, Daniel and I drove down to Ensenada, Mexico to speak and coach at DjangoGirls Ensenada. It was a 2-day workshop for women of any level of experience to get a taste of web application development.</p>
<blockquote class="instagram-media" data-instgrm-captioned="" data-instgrm-version="4" style="background: #FFF; border-radius: 3px; border: 0; box-shadow: 0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width: 658px; padding: 0; width: -webkit-calc(100% - 2px); width: 99.375%; width: calc(100% - 2px);">
<div style="padding: 8px;">
<div style="background: #F8F8F8; line-height: 0; margin-top: 40px; padding: 50% 0; text-align: center; width: 100%;">
<div style="background: url(data:image/png; display: block; height: 44px; margin: 0 auto -44px; position: relative; top: -22px; width: 44px;">
</div>
</div>
<div style="margin: 8px 0 0 0; padding: 0 4px;">
<a href="https://instagram.com/p/3DWoBhxYBj/" style="color: black; font-family: Arial,sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 17px; text-decoration: none; word-wrap: break-word;" target="_top">Thank you to the @DjangoGirlsMX organizer team!</a></div>
<div style="color: #c9c8cd; font-family: Arial,sans-serif; font-size: 14px; line-height: 17px; margin-bottom: 0; margin-top: 8px; overflow: hidden; padding: 8px 0 7px; text-align: center; text-overflow: ellipsis; white-space: nowrap;">
A photo posted by Audrey Roy Greenfeld (@pyaudrey) on <time datetime="2015-05-24T05:09:07+00:00" style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;">May 23, 2015 at 10:09pm PDT</time></div>
</div>
</blockquote>
<script async="" defer="" src="//platform.instagram.com/en_US/embeds.js"></script>
<p>The event was organized by <a href="https://twitter.com/djangogirlsmx">DjangoGirlsMX</a> with the help of the <a href="http://tijuana.usconsulate.gov/">US Consulate General of Tijuana</a> and the non-profit <a href="http://halaken.org/">Hala Ken</a>.</p>
<p>We asked the US Consulate and Hala Ken about why they decided to get involved. They answered that Django Girls workshops fit perfectly into two of their major areas of interest: new technology and women's empowerment.</p>
<p>We were honored to be invited as guest speakers and appreciative of the opportunity, knowing that we could make a big difference showing women new to Django that we cared.</p>
<blockquote class="twitter-tweet" lang="en">
<div dir="ltr" lang="es">
¡Como invitados especiales tendremos a <a href="https://twitter.com/audreyr">@audreyr</a> y <a href="https://twitter.com/pydanny">@pydanny</a>! :D <a href="https://twitter.com/hashtag/DjangoGirls?src=hash">#DjangoGirls</a> <a href="https://twitter.com/hashtag/MondayMotivation?src=hash">#MondayMotivation</a> <a href="http://t.co/74rFq7DXEN">pic.twitter.com/74rFq7DXEN</a></div>
— DjangoGirls Ensenada (@DjangoGirlsMX) <a href="https://twitter.com/DjangoGirlsMX/status/590243465918750722">April 20, 2015</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>
<p>At the end of the morning session, we gave a talk to inspire attendees to keep going with their programming journey. It was called "Programming Gives You Superpowers." Here are the slides.</p>
<iframe src="//www.slideshare.net/slideshow/embed_code/key/5bBUFTEvDYqcIn" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/twoscoopspress/programming-gives-you-superpowers" title="Programming Gives You Superpowers" target="_blank">Programming Gives You Superpowers</a> </strong> from <strong><a href="//www.slideshare.net/twoscoopspress" target="_blank">Audrey & Daniel Roy Greenfeld</a></strong> </div>
<p>Note: for fun we made the cover image a little fancier after the talk, otherwise it's the same :)</p>
<p>It was a fantastic experience getting to spend time with the web development community of Tijuana/Ensenada. So many of the Python Tijuana and Django Girls Tijuana organizers and members drove out to Ensenada and spent the night in hotels to help make this happen. We had fun coaching alongside them after our talk.</p>
<p>My co-author, co-presenter, co-everything husband PyDanny also blogged his account of it: <a href="http://www.pydanny.com/my-first-djangogirls-event.html">My First Django Girls Event</a></p>
<p>Finally, we had such a great time that we're now working on planning an upcoming Inland Empire DjangoGirls/RailsGirls event. All are invited to help: RSVP <a href="http://www.meetup.com/iepyladies/events/222160303/">here</a> or <a href="http://www.meetup.com/iepython/events/222819119/">here</a> for the May 30 planning session.</p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-730060959537810841.post-15475867310144826852015-05-03T17:12:00.001-07:002015-05-03T17:12:23.368-07:00Two Scoops of Django 1.8 is out!Daniel Roy Greenfeld and I have updated Two Scoops of Django to 1.8, since Django 1.8 is a Long Term Support version.<br />
<br />
The book is now available as a PDF. I know this will make a lot of folks happy! The print paperback is coming soon (US and India editions to start).<br />
<br />
More info: <a href="http://twoscoopspress.org/products/two-scoops-of-django-1-8">http://twoscoopspress.org/products/two-scoops-of-django-1-8</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://twoscoops.smugmug.com/Two-Scoops-Press-Media-Kit/i-HZqTR3Z/0/XL/two-scoops-1.8-ebook-XL.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://twoscoops.smugmug.com/Two-Scoops-Press-Media-Kit/i-HZqTR3Z/0/XL/two-scoops-1.8-ebook-XL.jpg" height="480" width="640" /></a></div>
<br />
I wrote half the book, including some of the rather difficult parts :) I also did the illustrations. The book is filled with a ton of weird cartoons and silly humor.<br />
<br />
Enjoy, and hope it's helpful!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-82630146033454569312015-04-12T17:38:00.000-07:002015-04-12T22:11:26.100-07:00Spring Cleaning for Python ProgrammersIt's spring again, which means that for Python programmers, it's time to clean out your hard drive.<br />
<br />
Instructions:<br />
<br />
1. Add these lines to your .bashrc (or other shell rc) file:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #007020;">alias </span><span style="color: #996633;">rmpyc</span><span style="color: #333333;">=</span><span style="background-color: #fff0f0;">'find . -type f -name "*.pyc" -print -delete'</span>
<span style="color: #007020;">export </span><span style="color: #996633;">PYTHONDONTWRITEBYTECODE</span><span style="color: #333333;">=</span><span style="color: #007020;">true</span>
</pre>
</div>
<br />
The first part gives you a handy rmpyc command to recursively delete .pyc files.<br />
<br />
The second part tells Python not to write .pyc files anymore.<br />
<br />
2. Source your rc file and run rmpyc from your home directory (on UNIX, from ~). This will delete all the Python bytecode from your home dir onward. You don't need to keep it around because it'll just get rewritten as needed anyway.<br />
<br />
3. Delete the virtualenvs that you're not using. (e.g. if you use virtualenvwrapper, delete the directories in ~/.virtualenvs/ that you don't need).<br />
<br />
4. If you use VirtualBox, delete the virtual machines that you don't need.<br />
<br />
5. Delete the repos that you don't need around anymore.<br />
<br />
In my case I freed up 3 GB by removing the .pyc files and 25 GB by removing the virtual machines. I forgot to check how much space my unused virtualenvs took up, but it was probably a non-trivial amount.<br />
<br />
My numbers are probably higher than most because my laptop's almost 5 years old and I mess around with random Python packages a lot, but you should still be able to save some space. At the very least, it'll be like squeezing the last paste out of a toothpaste tube.<br />
<br />
<br />
Note: originally the instructions said the following, but I updated them after advice from <a href="https://twitter.com/lazlofruvous/status/587429126119763969">Dan Crosta</a>, <a href="https://twitter.com/glyph/status/587469903222939649">Glyph</a>, and <a href="https://twitter.com/wlonk/status/587431447222444033">Kit</a>. Thank you all so much for the tips!
<br />
<div>
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">alias rmpyc<span style="color: #333333;">=</span><span style="background-color: #fff0f0;">'find . -type f -name "*.pyc" -print0 | xargs -0 rm -v'</span>
</pre>
</div>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-79128485475166123992015-03-24T17:30:00.000-07:002015-04-10T10:49:24.655-07:00Pillow FlowersLately, I've been playing around with drawing flowers with Python and Pillow.<br />
<br />
The trick to drawing flowers is to iterate around the petals in polar coordinates, and then convert polar to cartesian for drawing purposes.<br />
<br />
<a href="https://www.flickr.com/photos/orangepulp/16296238814" title="Flower Experiment 1 by Audrey Roy, on Flickr"><img alt="Flower Experiment 1" height="256" src="https://farm9.staticflickr.com/8752/16296238814_1d04a0c4c2_o.png" width="268" /></a>
<a href="https://www.flickr.com/photos/orangepulp/16917616921" title="Flower Experiment 1 by Audrey Roy, on Flickr"><img alt="Flower Experiment 1" height="256" src="https://farm8.staticflickr.com/7283/16917616921_36c71f126d_o.png" width="268" /></a>
<a href="https://www.flickr.com/photos/orangepulp/16730958938" title="Flower Experiment 1 by Audrey Roy, on Flickr"><img alt="Flower Experiment 1" height="256" src="https://farm9.staticflickr.com/8732/16730958938_8f4b0f7c25_o.png" width="268" /></a>
<a href="https://www.flickr.com/photos/orangepulp/16711292217" title="Flower Experiment 1 by Audrey Roy, on Flickr"><img alt="Flower Experiment 1" height="256" src="https://farm8.staticflickr.com/7651/16711292217_92c13d2836_o.png" width="268" /></a>
<a href="https://www.flickr.com/photos/orangepulp/16730958918" title="Flower Experiment 1 by Audrey Roy, on Flickr"><img alt="Flower Experiment 1" height="256" src="https://farm8.staticflickr.com/7611/16730958918_90d2461703_o.png" width="268" /></a><br />
<br />
I demoed some of this at the meetup that I hosted last night, Inland Empire Pyladies' Coding for Artists. There, Danny and I taught participants how to use Pillow, ellipses, rectangles, random number generation, and trigonometry to make basic 2D generative art.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-32570621029691947222015-03-06T20:07:00.003-08:002015-03-06T20:30:20.614-08:00Compressing PDF Files at the Command Line<div>
Sometimes you have to email someone a bunch of PDF files, but you can't keep them in the same email without exceeding Gmail's 25MB limit (or the limit of whatever email service you use). </div>
<div>
<br /></div>
<div>
Rather than having to split up the email (which isn't ideal in many situations), you can compress the PDF files themselves. Here are a couple of strategies for doing that, both using the wonderful <a href="http://ghostscript.com/">Ghostscript</a> interpreter.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD0glZu5BLFQT0oor5v2vKqEk9bkm1gdkRfq7ZcoBzU96yXvPgMI4URp4yYNhK301rGBtvM7eqsymc_0KUXSx0cTJFNYFuLtgg9SC-pebJm4CzYjhZCIVO_r0cssXXD0Hf-X3AjpJ4buk/s1600/ghostscript-logo.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD0glZu5BLFQT0oor5v2vKqEk9bkm1gdkRfq7ZcoBzU96yXvPgMI4URp4yYNhK301rGBtvM7eqsymc_0KUXSx0cTJFNYFuLtgg9SC-pebJm4CzYjhZCIVO_r0cssXXD0Hf-X3AjpJ4buk/s1600/ghostscript-logo.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">You might remember Ghostscript from your college days. <br />It's one of those old school things that's still awesome.</td></tr>
</tbody></table>
<br /></div>
<div>
In the end, I used ps2pdf to reduce 28MB of PDF files down to 12MB.</div>
<div>
<br />
<h2>
ps2pdf</h2>
The command-line tool ps2pdf converts .ps files (Postscript) to .pdf using Ghostscript. But you can also pass in a PDF file as input.<br />
<br />
If you have Ghostscript installed, you can type this at the command line:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">ps2pdf -dPDFSETTINGS=/ebook in.pdf out.pdf
</pre>
</div>
<br />
The <span style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 12px; line-height: 18px; white-space: inherit;">/ebook</span> setting "selects medium-resolution output similar to the Acrobat Distiller "eBook" setting," which sounds good for documents that need to be screen-readable.<br />
<br />
<a href="http://tex.stackexchange.com/questions/6121/presentation-option-screen-or-prepress-in-ps2pdf">Read more on StackOverflow</a> about what else you can pass into PDFSETTINGS.<br />
<br />
<h2>
Color to Grayscale</h2>
<div>
Another easy way to get rid of a lot of unneeded PDF size is to convert it from color to grayscale. Color takes up a lot of space and is not needed for many documents.</div>
<div>
<br /></div>
If you have Ghostscript installed, you can type this at the command line:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">gs -sDEVICE=pdfwrite -sProcessColorModel=DeviceGray -sColorConversionStrategy=Gray -dOverrideICC -o out.pdf -f in.pdf
</pre>
</div>
<br />
Here, in.pdf is your input file, and out.pdf is your output file.<br />
<br />
<h2>
Did It Work?</h2>
The easiest way to tell if it worked is to list the files in the current directory by size:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">ls -sS
</pre>
</div>
<br />
Compare the size of the input PDF with the output PDF. The output PDF should be smaller, of course.<br />
<br />
<h2>
Other Tools</h2>
<div>
Here are some other tools to look into if you don't like either of the above approaches.<br />
<br /></div>
<h3>
pdfsizeopt</h3>
<br />
I always start out by trying to find a Python solution to problems, because then whatever I find becomes a handy little building block for me to use in other Python projects.<br />
<br />
Before trying either of the above, I attempted to compress the PDF files with the Python library pdfsizeopt. However, I ran into this error:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">error: Multivalent.jar not found. Make sure it is on the $PATH, or it is one of the files on the $CLASSPATH.
</pre>
</div>
<br />
I resolved that error by finding the latest Multivalent jar file on the official project page and putting it on my $PATH, but then I got this error:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">AssertionError: Multivalent failed (status)
</pre>
</div>
<br />
At that point I moved on. That said, if anyone knows how to get around that second error, I'd love to know. It would be great to get pdfsizeopt working.<br />
<br />
<h3>
Shrinkpdf</h3>
<br />
I also tried out Alfred Klomp's nice <a href="http://www.alfredklomp.com/programming/shrinkpdf/">shrinkpdf.sh</a> script. If you try it out, experiment with the resolution and the other parameters. (This is actually how I ended up with my color to grayscale solution above.) Study it and play around until you're satisfied with the output.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-9847911300190845722015-02-23T08:23:00.000-08:002015-03-02T20:38:06.532-08:00Recap: Python and Pyladies at SCALE13xThis weekend was the 13th annual SoCal Linux Expo, or SCALE13x. It's held every year at the LAX Hilton.<br />
<br />
The Python and Pyladies community booths were side-by-side, with both booths representing members of various Python and Pyladies user groups throughout Southern California.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJE1uRNNJnpVt5QVi3QpVs2wgFysvztRi6m5rP8Ru5lpjjbL7Pbb6KTl86KdRaoJ8MjKH29WOhMNiQR0j1h_MKs7o8o_BXN1ayyETbPTjOE_pyS7jU74yMbM20uQ8U-pd7cioBfgJrP2U/s1600/python-pyladies-scale13x.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJE1uRNNJnpVt5QVi3QpVs2wgFysvztRi6m5rP8Ru5lpjjbL7Pbb6KTl86KdRaoJ8MjKH29WOhMNiQR0j1h_MKs7o8o_BXN1ayyETbPTjOE_pyS7jU74yMbM20uQ8U-pd7cioBfgJrP2U/s1600/python-pyladies-scale13x.png" height="320" style="cursor: move;" width="259" /></a></div>
<br />
I spent most of my time on the left side of the Pyladies booth, while Daniel was next door on the right side of the Python booth. Sometimes we crossed over into the opposite booths :)<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvb1NdAMfEnjtBxuXzSQTLcRdxHfbSnJ_PXCvBO3QIhjABtam61xCp9V6uvwjObtIGRfodzP4JmnfpCjjaOuyd5DBqpgBUFiF8mGLham2cdIlG25-Mj4edQ6R9TUKzlvgJGSqOYHnV5cI/s1600/audrey-daniel.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvb1NdAMfEnjtBxuXzSQTLcRdxHfbSnJ_PXCvBO3QIhjABtam61xCp9V6uvwjObtIGRfodzP4JmnfpCjjaOuyd5DBqpgBUFiF8mGLham2cdIlG25-Mj4edQ6R9TUKzlvgJGSqOYHnV5cI/s1600/audrey-daniel.jpg" height="640" width="478" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Audrey and Daniel</td></tr>
</tbody></table>
The setup sounds formal, but it was more like a friendly Python user get-together at the booths. People often came and stayed just to hang out. It was great to catch up with friends whom I haven't seen in a couple of years. We also got the opportunity to invite a lot of people who came by to future Python/Pyladies meetups throughout SoCal.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjonGA-iP2smCP0jvxn9N03-SjsMNEeg15TqqZ_Iefa0b1k0yaxGdj2uvMTOuQvvgdHa-mQAFy4ngkcC-45ALFiMZLZUCqUEBr-zvZjiKqaF_n3QkdmLRzFHM7hzbkcXA3rvtuAQVuAMtU/s1600/esther-audrey-tiffany.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjonGA-iP2smCP0jvxn9N03-SjsMNEeg15TqqZ_Iefa0b1k0yaxGdj2uvMTOuQvvgdHa-mQAFy4ngkcC-45ALFiMZLZUCqUEBr-zvZjiKqaF_n3QkdmLRzFHM7hzbkcXA3rvtuAQVuAMtU/s1600/esther-audrey-tiffany.jpg" height="480" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Esther of Pyladies LA & Burbank, Audrey and Tiffany of Inland Empire Pyladies</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhm1FwfLsISeXh1eFN3aw_5TucumokTwEZCzRm78ucsd7lcPq52N0czCORVH0sLOddM8aQntvscDyRzqmvxaehPznrs99XspfCo_L60JMXPw9jGLa3HDYud9wvrlU9xgM2wDSVDM2rb1Xg/s1600/greenfelds-debra-carol.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhm1FwfLsISeXh1eFN3aw_5TucumokTwEZCzRm78ucsd7lcPq52N0czCORVH0sLOddM8aQntvscDyRzqmvxaehPznrs99XspfCo_L60JMXPw9jGLa3HDYud9wvrlU9xgM2wDSVDM2rb1Xg/s1600/greenfelds-debra-carol.jpg" height="480" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Audrey and Daniel (that's us!), Debra, and Carol (San Diego Pyladies co-organizer).</td></tr>
</tbody></table>
<br />
Special thanks to goodwill for organizing the Python booth, and to <a href="http://willingconsulting.com/">Carol Willing</a> for organizing the Pyladies booth.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-61019872195174534432015-01-03T14:00:00.000-08:002015-02-24T08:32:34.981-08:00How to Add Syntax-Highlighted Code Snippets to Blog PostsI was looking for a quick, ultra-lightweight way to add code snippets to blog posts. My requirements were:<br />
<ol>
<li>The tool must not require me to embed a third-party widget into my blog post. That rules out Github Gists.</li>
<li>The tool must not require me to make changes to my main blog template, especially not changes that load any additional files. That rules out SyntaxHighlighter.</li>
</ol>
Now, don't get me wrong, Gists and SyntaxHighlighter are both awesome tools that I like to use for other purposes.<br />
<br />
I found a tool called <a href="http://hilite.me/">hilite.me</a> by <a href="http://kojevnikov.com/">Alexander Kojevnikov</a> which met the above criteria. It's pretty awesome:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3clidaoilnX2tynpgCCiOsP-sQdAcGADaWDOdk_tjaE2g_XBASpberkBss_1XbExbVBVMU2ebYqQbODRYUTvbyEzfcpqD52kYaqMkTa9RW8HsyG6Z5qBd7RLofoIB6hCgICx6__l_a90/s1600/hilite.me.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3clidaoilnX2tynpgCCiOsP-sQdAcGADaWDOdk_tjaE2g_XBASpberkBss_1XbExbVBVMU2ebYqQbODRYUTvbyEzfcpqD52kYaqMkTa9RW8HsyG6Z5qBd7RLofoIB6hCgICx6__l_a90/s1600/hilite.me.png" height="500" width="640" /></a></div>
Now, adding code blocks to blog posts is easy:<br />
<ol>
<li>Paste your source code into the left box</li>
<li>Click Highlight!</li>
<li>Copy the generated HTML/CSS code into the HTML for your blog post. (For example, if you're on Blogger, click HTML next to Compose and then paste the code into its place.)</li>
<li>Preview and edit to make sure that the in-between whitespace looks decent.</li>
</ol>
<div>
As a bonus, it's open source, and it's powered by Flask and Pygments, two other nice open-source Python packages.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-14491236195168015202014-12-14T07:30:00.000-08:002015-02-24T08:28:38.071-08:00Customizing the Bash Prompt to Be More RomanticI used to have a long, detailed Bash prompt like this:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #c65d09; font-weight: bold;">happycode:open-source-repos audreyr$</span>
</pre>
</div>
<br />
To get this, I had this line in my .bashrc file:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #eeeedd; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<table><tbody>
<tr><td><pre style="line-height: 125%; margin: 0;">1
2</pre>
</td><td><pre style="line-height: 125%; margin: 0;"><span style="color: forestgreen;"># Bash prompt displays the hostname, current directory name, username</span>
<span style="color: #00688b;">PS1</span>=<span style="color: #cd5555;">"\h:\W \u\$"</span>
</pre>
</td></tr>
</tbody></table>
</div>
<br />
But I eventually grew tired of seeing all that useful information, as it made my prompt too long. It was time for a change.<br />
<br />
I thought about what I wanted to be reminded of every time I opened my terminal. Something happy, I thought. The happiest thing is love.<br />
<br />
So I changed it to this:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #eeeedd; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<table><tbody>
<tr><td><pre style="line-height: 125%; margin: 0;">1
2</pre>
</td><td><pre style="line-height: 125%; margin: 0;"><span style="color: forestgreen;"># More romantic bash prompt.</span>
<span style="color: #00688b;">PS1</span>=<span style="color: #cd5555;">"\W a♥d "</span>
</pre>
</td></tr>
</tbody></table>
</div>
<br />
That's short for Audrey ♥ Daniel, of course.<br />
<br />
You can use other Unicode symbols in your Bash prompt to jazz it up, like happy faces and flowers. See <a href="http://unix.stackexchange.com/questions/25903/awesome-symbols-and-characters-in-a-bash-prompt">this post</a> on the Unix StackExchange about someone with an awesome ★ in their prompt.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-36678849972668437052014-09-15T12:37:00.000-07:002015-02-10T00:17:06.315-08:00Free Intro to Python Workshop in San Diego on Sat, Sept 20I'm co-hosting a <strong>free</strong> Python programming workshop this weekend. Whether you've never coded before or have some coding background, this workshop if for you.<br />
<br />
Saturday, September 20, 2014<br />
9:30am-4pm<br />
Ansir Innovation Center, San Diego, CA<br />
<br />
<img alt="intro-to-python-workshop-san-diego" class="alignnone size-full wp-image-234" src="http://audreyr.files.wordpress.com/2014/09/intro-to-python-workshop-san-diego.png" height="300" width="474" /><br />
<br />
As a woman programmer, I encourage more women to sign up and fill the remaining spaces!
There are still spaces available through Inland Empire Pyladies and San Diego Pyladies. RSVP through the Pyladies chapter closest to you:
<br />
<ul>
<li><a href="http://www.meetup.com/iepyladies/events/202775112/" target="_blank" title="IE Pyladies RSVP page">http://www.meetup.com/iepyladies/events/202775112/</a></li>
<li><a href="http://www.meetup.com/sd-pyladies/events/199295472/" target="_blank" title="SD Pyladies RSVP page">http://www.meetup.com/sd-pyladies/events/199295472/</a></li>
</ul>
If one of the above fills up, it's okay to RSVP through the other.<br />
<br />
This is a joint event between San Diego Python/Pyladies and Inland Empire Python/Pyladies. It is also affiliated with OpenHatch and sponsored by the Python Software Foundation.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-12766452231130764012014-04-26T10:14:00.000-07:002015-02-10T00:14:54.886-08:00jQuery MessageBar: A Top Bar For NotificationsLast year, Danny and I did a major, major overhaul of the <a href="http://www.djangopackages.com/" target="_blank" title="Django Packages">Django Packages</a> website. During this effort, I extracted some of the interesting JS/CSS functionality into freestanding jQuery/JS plugins of their own.<br />
<br />
One of the fruits of this effort was a jQuery plugin called MessageBar.
Here's what it looks like when it shows up on Django Packages:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img alt="Screenshot of jQuery MessageBar on Django Packages" class="wp-image-222 size-medium" src="http://audreyr.files.wordpress.com/2014/04/messagebar-screenshot.png?w=474" height="68" style="margin-left: auto; margin-right: auto;" width="474" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">jQuery MessageBar, as seen at the top of Django Packages</td></tr>
</tbody></table>
<br />
Consider this a gift to the front-end developer community. It is free and open-source, available on GitHub: <a href="https://github.com/audreyr/messagebar" target="_blank" title="jQuery MessageBar">https://github.com/audreyr/messagebar</a><br />
<br />
It works particularly nicely with Django's messages framework, but Django isn't required. It's just HTML, CSS, and JS. Therefore, it would work just fine with any PHP or Ruby site, for example.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-48444368250448804902014-02-28T10:25:00.000-08:002015-02-10T00:12:05.609-08:00Cookiecutter Hits 704 Stars on GitHubOne of my largest open source projects, <a href="https://github.com/audreyr/cookiecutter" target="_blank" title="Cookiecutter">Cookiecutter</a>, has surpassed the 700-star milestone.<br />
<br />
<img alt="Cookiecutter logo" class="alignnone size-medium wp-image-211" src="http://audreyr.files.wordpress.com/2014/02/cookiecutter_medium.png?w=474" height="90" width="474" /><br />
<br />
In case you aren't familiar with it, Cookiecutter is a utility for generating projects from project templates. It is language-agnostic, and there are boilerplate templates for HTML, JS, C, Python, Django, LaTeX, Common Lisp, and other types of projects.
<br />
<h2>
Development Continues</h2>
Slowly but surely, I have been working through the queue of pull requests for <a href="https://github.com/audreyr/cookiecutter" target="_blank" title="A command-line utility that creates projects from cookiecutters (project templates). E.g. Python package projects, jQuery plugin projects.">Cookiecutter</a> and <a href="https://github.com/audreyr/cookiecutter-pypackage" target="_blank">cookiecutter-pypackage</a>. Reviewing pull requests takes time because:
<br />
<ul>
<li>Every patch must be carefully reviewed for cross-platform compatibility (Windows, Linux, Mac).</li>
<li>Once a patch goes in, it's in the codebase forever. Each line of code requires thoughtful consideration.</li>
<li>Contributors to Cookiecutter are generally very experienced programmers that push the limits of my knowledge.</li>
<li>To meet my standards, each patch typically requires hours of coding on my part. This is to fix cross-platform issues, cross-Python-version compatibility issues, ensure uniform coding style, hunt down any edge cases that may have been missed, etc. It's a labor of love.</li>
</ul>
Open source projects benefit greatly from having a leader who understand every piece that goes into the project, and who has a vision for the project's functionality, coding style, and growth. I look forward to having the opportunity to develop my vision in the coming months.
<br />
<h2>
Sponsorship Opportunity</h2>
If your company uses Cookiecutter, consider sponsoring the project. Benefits include:
<br />
<ul>
<li>Advertising and exposure on the Cookiecutter README and the documentation homepage.</li>
<li>Extra attention given to issues/pull requests from your company's developers.</li>
<li>Additional development work on Cookiecutter, focused on your company's needs.</li>
<li>The gratification of giving back to open source, and full bragging rights. This makes for great company PR.</li>
<li>Mentions of your company on future blog posts about Cookiecutter, in the release notes, and more.</li>
</ul>
For more information, email me at aroy at alum.mit.edu.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-64329202912824701922014-02-20T10:44:00.000-08:002015-02-10T00:09:20.634-08:00Art Donated to PyCon Philippines<a href="http://twoscoops.smugmug.com/February-2014/Logging-Levels-Art-Print/i-b8ffkzJ/A"><img alt="Art Donated to PyCon Philippines" class="size-full" src="http://audreyr.files.wordpress.com/2014/02/log-levels-ice-cream.jpg" height="480" width="640" /></a><br />
<br />
I hand-painted this art print in watercolor, which will be raffled off as a prize at <a href="http://ph.pycon.org/" target="_blank">PyCon Philippines 2014</a>. The painting shows the various logging levels, as applied to ice cream.<br />
<br />
See a larger photo <a href="http://twoscoops.smugmug.com/February-2014/Logging-Levels-Art-Print/i-b8ffkzJ/A" target="_blank" title="Logging Levels in Ice Cream by Audrey Roy">here</a>.<br />
<br />
This was part of Two Scoops Press' <a href="http://twoscoopspress.org/pages/conference-sponsorship-program" target="_blank">in-kind conference sponsorship package</a> for PyCon Philippines.
<br />
<h2>
Cheers and Congratulations</h2>
I can't even begin to express how thrilled I am about the growth and success of PyCon Philippines.
<br />
<br />
My mother was born and raised in the Philippines, and I lived there for part of my childhood. I still visit family in the Philippines often. I am very proud to consider myself a part of the technical community there.<br />
<br />
As a past co-organizer who worked like crazy in 2012 creating and managing the previous PyCon PH website, doing PR, running the live coverage Twitter stream, and helping with sponsorships, I know how hard the organizing team is working and salute your efforts.<br />
<br />
In particular, special shout-outs to these PyCon PH & Python community leaders:
<br />
<ul>
<li><a href="http://www.youtube.com/watch?v=VDiV07S15L4">Sony Valdez</a>, Conference Chair</li>
<li><a href="https://github.com/shimofuri">Andrew Paulo Robillo</a>, <a href="https://speakerdeck.com/regalandroid">Ralph Vincent Regalado</a> and <a href="http://www.linkedin.com/pub/briane-paul-samson/1a/422/671">Briane Paul Samson</a></li>
<li><a href="http://marksteve.com/">Mark Steve Samson</a> and <a href="http://skytreader.net/">Chad Estioco</a></li>
<li><a href="http://blog.mattlebrun.com/">Matt Lebrun</a> and <a href="https://twitter.com/codemickeycode">Micaela Reyes</a></li>
<li><a href="https://www.facebook.com/Auberonsolutions">Frank and Ann Pohlmann</a>, for passing the torch after chairing PyCon PH 2012</li>
</ul>
I urge everyone to show their gratitude. I think I speak for everyone in the Python community when I say that your efforts are not only appreciated, but also recognized internationally.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-22922339440917432132014-02-12T21:39:00.000-08:002015-02-10T00:05:38.047-08:00Two Scoops of Django 1.6 is a #1 Python BestsellerLast week, Danny and I released our new book, <a href="http://amzn.to/1n98duC" target="_blank" title="Two Scoops of Django on Amazon.com">Two Scoops of Django: Best Practices for Django 1.6</a>.
<br />
<h2>
Bestseller Status</h2>
We made it as a #1 bestseller in the Programming and in the Python categories on Amazon.
The 1.6 edition is still a "#1 Python Bestseller" as of now:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDXNtBLPBVUtBdf2wAt6sgBt_6oDpHifX7y6Eh2QJEYBKg4kEYxUutHB5VHsUACeWd8cH5eCbJFthJJQIcuAGlLDACLpfUQUo45xJXe0hUMZVWytvLpzGXTk-SYIufAPYPL2R_OJh3hLI/s1600/no-1-python-bestseller-cropped.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDXNtBLPBVUtBdf2wAt6sgBt_6oDpHifX7y6Eh2QJEYBKg4kEYxUutHB5VHsUACeWd8cH5eCbJFthJJQIcuAGlLDACLpfUQUo45xJXe0hUMZVWytvLpzGXTk-SYIufAPYPL2R_OJh3hLI/s1600/no-1-python-bestseller-cropped.png" height="122" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Screenshot from Amazon showing our book as a #1 Python bestseller.</td></tr>
</tbody></table>
<br />
At one point, it even ranked #33 overall in Amazon's entire Education & Reference Books category.
This is what happens when you write thoughtfully, with careful attention to detail, and with strong intentions to create a truly helpful reference book.
<br />
<h2>
Huge Thank You to Supporters</h2>
Response from buyers of the 1.6 edition has been overwhelmingly positive and glowing. We are getting your emails and messages, and they really touch our hearts and mean a lot to us.
We released the 1.5 edition over a year ago. The 1.6 edition took several additional months of work. The positive response from readers is what has made all of our effort worthwhile.
Special thanks to all who reviewed the book on Amazon.com and other international Amazon sites.
<br />
<h2>
Autographed Copies</h2>
Last week, we autographed around 50 copies and mailed them out around the world. This was a lot of fun. Some of the copies may be autographed a bit wildly, with completely wacky humor. We got a bit carried away, doing our best to put a lot of thought and care into sending each package.
For anyone who didn't get an autographed copy:
<br />
<ul>
<li>We are waiting for another shipment of books to arrive on our doorstep. Once it arrives, we'll have more available through <a href="http://twoscoopspress.org/products/two-scoops-of-django-1-6" target="_blank">twoscoopspress.org</a>.</li>
<li>We're also happy to autograph books in person. We won't be able to make it to conferences this year, but we're thinking of doing a road trip at some point and might visit some user groups.</li>
</ul>
<h2>
The Last of the Series</h2>
As we mentioned, the 1.6 edition is the last Two Scoops of Django book that we will ever create. It is a major expansion and rewrite of the book, with countless tips from readers incorporated into the material.
It was a major effort, and we are incredibly proud of the results. But we are tired now, and we are done. Our time has come to move on to other great things.
<br />
<h2>
Our Long History of Listening to Feedback</h2>
We have updated the <a href="http://twoscoopspress.com/pages/two-scoops-of-django-1-6-faq">FAQ</a> again with more answers to common questions. To save us from typing and repetitive stress injury pain, please check there first if you have a common question or comment.
Remember, we have a long, long history of <strong>always</strong> listening to reader feedback and trying our best, but that our physical and mental health, family, and work have to come first.<br />
<br />
We will continue to try our best, but please understand we're backlogged with work and family obligations (not to mention open source project maintenance obligations). Right now, getting our lives in order is our top priority.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-5923301233275548372013-12-04T23:40:00.000-08:002015-02-09T23:42:20.986-08:00Bad Python Jokes for the HolidaysHow does Santa check his lists to see who's been naughty or nice?<br />
<br />
<script src="https://gist.github.com/audreyr/7760325.js"></script>
What Christmas items can be used as context managers?<br />
<br />
<script src="https://gist.github.com/audreyr/7760444.js"></script>
How do Python programmers make Hanukkah latkes?<br />
<br />
<script src="https://gist.github.com/audreyr/7760709.js"></script>
Finally, a Hanukkah puzzle from <a href="http://www.pydanny.com/">PyDanny</a>, who's really been getting into the holiday cheer:<br />
<br />
<script src="https://gist.github.com/pydanny/7760823.js"></script>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-58588261681971013662013-12-02T13:18:00.000-08:002015-02-09T23:34:04.693-08:00The Python Indie Bundle<strong>Update: the promotion has ended. Thanks, everyone, for your support and help getting word out.</strong><br />
<br />
To celebrate Cyber Monday 2013, I've teamed up with Daniel Greenfeld and Matt Harrison to create the Python Indie Bundle.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj16FQxsPw0IwFIzqMKXdEPVaepAvrlpq4jyOel13zXu9KgvnT2ALWSND7hRfFyMTkkMvI30A-QzowV1dT2sOG1FwHTd3vF_qWf2jhuC2OcuK2eleQW78xSTN2NrkoOMKnCl2zmmjBM9DM/s1600/python-indie-bundle.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj16FQxsPw0IwFIzqMKXdEPVaepAvrlpq4jyOel13zXu9KgvnT2ALWSND7hRfFyMTkkMvI30A-QzowV1dT2sOG1FwHTd3vF_qWf2jhuC2OcuK2eleQW78xSTN2NrkoOMKnCl2zmmjBM9DM/s1600/python-indie-bundle.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Treading on Python volumes 1 and 2, and Two Scoops of Django 1.5.</td></tr>
</tbody></table>
<br />
The set of 3 books normally costs $36.95 total, but for Cyber Monday we're offering it for $24.95.
You can get it from Two Scoops Press <a href="http://twoscoopspress.org/products/python-indie-bundle" target="_blank" title="Two Scoops Press: Python Indie Bundle">here</a>.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-730060959537810841.post-81021278133081744392006-08-30T07:14:00.000-07:002015-02-09T17:59:15.708-08:00Hello world!Hi. This is going to be my blog one day.<br />
<br />
It will be full of everything that I like: clouds, rainbows, oranges, painting, sculpture, programming, Python, JavaScript, open source, design, travel, dogs, butterflies, cooking, baking, ice hockey, sewing, ice cream, and possibly even someone named Danny whom I haven't met yet.<br />
<br />
But not yet.Unknownnoreply@blogger.com0