<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>FatTuba.com</title><link href="//fattuba.com/" rel="alternate"></link><link href="//fattuba.com/feeds/all.atom.xml" rel="self"></link><id>//fattuba.com/</id><updated>2024-11-03T00:00:00-05:00</updated><entry><title>Improve Email Deliverability From Debian</title><link href="//fattuba.com/improve-email-deliverability-from-debian" rel="alternate"></link><updated>2024-11-03T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2024-11-03:improve-email-deliverability-from-debian</id><summary type="html">&lt;p&gt;When you say that you want to host your own email, you&amp;#8217;ll hear the
standard line from folks: &amp;#8220;Don&amp;#8217;t do it.  It&amp;#8217;s too hard.&amp;#8221;  When they say
&amp;#8220;it&amp;#8217;s too hard&amp;#8221; what they mean is that it&amp;#8217;s too hard to ensure that
email you send will actually end up in other people&amp;#8217;s&amp;nbsp;inboxes.&lt;/p&gt;
&lt;p&gt;If you persist, they&amp;#8217;ll advise you to configure your email server with
&lt;span class="caps"&gt;SPF&lt;/span&gt;, &lt;span class="caps"&gt;DKIM&lt;/span&gt;, and &lt;span class="caps"&gt;DMARC&lt;/span&gt;.  Oh and also make sure the &lt;span class="caps"&gt;IP&lt;/span&gt; address of your
email server isn&amp;#8217;t on a&amp;nbsp;blacklist.&lt;/p&gt;
&lt;p&gt;After you complete these recommended incantations and start using your
shiny new email server, you&amp;#8217;ll notice that your email recipients on gmail
don&amp;#8217;t see your emails in their inbox.  After reaching out, your recipients
will tell you your email ended up in their gmail spam folder.  So you
start searching the internet for answers.  &amp;#8220;How do I keep my email from
ending up in gmail spam folder?&amp;#8221;  The answers all pretty much point back
to the incantations above.  So to double check your setup you use a
service like &lt;a href="https://mxtoolbox.com"&gt;mxtoolbox&lt;/a&gt; and everything looks
pretty&amp;nbsp;good.&lt;/p&gt;
&lt;p&gt;So now what?  Do you give up on the distributed nature of &lt;span class="caps"&gt;SMTP&lt;/span&gt; and let Google/Apple/Microsoft &lt;a href="https://www.sellcell.com/blog/most-popular-email-provider-by-number-of-users/"&gt;oligopoly&lt;/a&gt; host your email like 90% of the internet?  Be stubborn and resign yourself to the fact that you&amp;#8217;ll need to utter the words &amp;#8220;have you checked your spam folder&amp;#8221; on a regular&amp;nbsp;basis?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;lt;sarcasm&amp;gt;&lt;/em&gt;I&amp;#8217;m sure it&amp;#8217;s shocking to folks that know
me&lt;em&gt;&amp;lt;/sarcasm&amp;gt;&lt;/em&gt; that I&amp;#8217;m squarely in the second camp: stubborn and
stuck in spam&amp;nbsp;folders.&lt;/p&gt;
&lt;p&gt;Because I&amp;#8217;m a counter-cultural rebel, I&amp;#8217;m burying the click bait tag
line here instead of in the title: &lt;strong&gt;Here&amp;#8217;s one weird trick to improve
email&amp;nbsp;deliverability!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hide your &lt;code&gt;Received&lt;/code&gt; headers.  That&amp;#8217;s it.  Once I started doing that a
few days ago, my email started showing up in my buddy&amp;#8217;s inbox instead of
his spam folder.  It makes sense I suppose.  Google looks for suspicious
&lt;span class="caps"&gt;IP&lt;/span&gt; addresses sending email so even though you&amp;#8217;re relaying email through
a smarthost with a trusted &lt;span class="caps"&gt;IP&lt;/span&gt; address, your untrusted dynamic &lt;span class="caps"&gt;IP&lt;/span&gt; from
your home &lt;span class="caps"&gt;ISP&lt;/span&gt; is still visible in a Received header.  So hiding those
headers leaks less information to Google that they can use against&amp;nbsp;you.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s how to hide Received headers if you&amp;#8217;re using Exim on Debian as
your email&amp;nbsp;server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;myemailserver&lt;/span&gt;&lt;span class="o"&gt;:~&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;grep&lt;/span&gt; &lt;span class="n"&gt;Received&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;exim4&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mo"&gt;000&lt;/span&gt;&lt;span class="n"&gt;_localmacros&lt;/span&gt;
&lt;span class="n"&gt;REMOTE_SMTP_TRANSPORTS_HEADERS_REMOVE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Received&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Anyway, enjoy folks actually receiving your&amp;nbsp;email!&lt;/p&gt;</summary></entry><entry><title>Pry URLs From My Cold, Dead Hands</title><link href="//fattuba.com/pry-urls-from-my-cold-dead-hands" rel="alternate"></link><updated>2020-11-16T00:00:00-06:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2020-11-16:pry-urls-from-my-cold-dead-hands</id><summary type="html">&lt;p&gt;Google is trying to &lt;a href="https://www.androidpolice.com/2020/08/13/google-resumes-its-senseless-attack-on-the-url-bar-hides-full-addresses-on-chrome-canary/"&gt;hide the &lt;span class="caps"&gt;URL&lt;/span&gt; in Chrome&lt;/a&gt;.  This &lt;a href="https://www.pcworld.com/article/228378/Chrome_Firefox_Experiment_With_Hidden_URL_Bars.html"&gt;has been tried before&lt;/a&gt; in various browsers at various&amp;nbsp;times.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m deeply unhappy about this, not because of Google&amp;#8217;s move in Chrome but
because of the general trend in the industry to make the &lt;span class="caps"&gt;URL&lt;/span&gt; a second
class&amp;nbsp;citizen.&lt;/p&gt;
&lt;p&gt;The current worst examples of this is an Electron app that I use everyday:
Slack.  When I run Slack in the browser I get the benefit of being able
to see multiple conversations at a time(multiple browser windows), or
be able to jump between conversations quickly(multiple browser tabs).
Another advantage of running Slack in the browser is that I can easily
share links to comments/channels without having to dig around for the
&amp;#8220;Copy Link&amp;#8221; or &amp;#8220;Share&amp;#8221;&amp;nbsp;button.&lt;/p&gt;
&lt;p&gt;In fact, the only reason to fire up Slack as an Electron app is to share my
screen.  Loading Slack in the browser wins every other time &amp;#8216;cause&amp;nbsp;URLs.&lt;/p&gt;</summary></entry><entry><title>Markdown for Microsoft Word</title><link href="//fattuba.com/markdown-for-microsoft-word" rel="alternate"></link><updated>2019-08-17T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2019-08-17:markdown-for-microsoft-word</id><summary type="html">&lt;p&gt;&lt;a href="http://www.catb.org/~esr/writings/taoup/html/ch18s02.html"&gt;If you prefer text-based documentation(markdown, etc) over &lt;span class="caps"&gt;WYSIWYG&lt;/span&gt;(&lt;span class="caps"&gt;MS&lt;/span&gt;
Word)&lt;/a&gt;, then
this is for you!  If you work in modern corporate America, Microsoft
Word is unavoidable.  Even though I wish it weren&amp;#8217;t so, most engineering
organizations use it for technical&amp;nbsp;documentation.&lt;/p&gt;
&lt;p&gt;I want to use markdown as the source and then automatically render
Microsoft Word.  Most folks online recommend pandoc for this kind
of thing.  The problem is that pandoc renders docx using it&amp;#8217;s own&amp;nbsp;style.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But there&amp;#8217;s usually a standard corporate template that you&amp;#8217;re required
to&amp;nbsp;use.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At this point I usually stare blankly at my screen for a couple of minutes then
begrudgingly load up the corporate template in Word and start
copy-n-past&amp;#8217;ing then manually formatting.  But&amp;#8230;. no&amp;#8230;.&amp;nbsp;longer.&lt;/p&gt;
&lt;p&gt;I came across a little hack that works pretty well.  If you insert unstyled &lt;span class="caps"&gt;HTML&lt;/span&gt;
into a Word template using &lt;code&gt;Insert-&amp;gt;Object-&amp;gt;Text from File&lt;/code&gt;, Word will
automatically apply the template&amp;nbsp;styles.&lt;/p&gt;
&lt;p&gt;So, I generate &lt;span class="caps"&gt;HTML&lt;/span&gt; from markdown: &lt;code&gt;pandoc -s my.md -o my.html&lt;/code&gt;, then insert the
&lt;span class="caps"&gt;HTML&lt;/span&gt; using the &lt;code&gt;Text from File&lt;/code&gt; menu item in Word and voila!  I have a
perfectly formatted Word docx using the corporate template&amp;nbsp;automatically.&lt;/p&gt;
&lt;p&gt;However, it&amp;#8217;s still a hassle to have to perform these manual menu selections
everything I want to re-render the docx.  So here&amp;#8217;s a little Python script to
automate that part using &lt;span class="caps"&gt;COM&lt;/span&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;

&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;win32com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;win32&lt;/span&gt;
&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;win32com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;constants&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;


&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;argv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;

    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;convert html to docx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;htmlfile&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;the html file path for input&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;docxfile&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;the docx file path for output&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-t&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;--template&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;template.dotx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;the template file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;template_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;html_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;htmlfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;docx_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docxfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;win32&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gencache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnsureDispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;try:&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Visible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;True&lt;/span&gt;
        &lt;span class="n"&gt;section&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InsertFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TablesOfContents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InlineShapes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InlineShapes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;LinkFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BreakLink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveAs2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docx_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wdFormatXMLDocument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;finally:&lt;/span&gt;
        &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It also regenerates the table of contents and embeds the images after the
text is inserted.  I hope this helps any other text-based documentation
lovers out&amp;nbsp;there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2019/9/13 Update: We&amp;#8217;re now embedding all the images instead of leaving them
as&amp;nbsp;links.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2020/6/15 Update: It turns out that this method doesn&amp;#8217;t work in the general
case.  It happens to work with my template because the template fonts are close
enough to the default Word fonts that they&amp;nbsp;match.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2024/9/14 Update: Perhaps &lt;a href="https://stackoverflow.com/a/53140251/226697"&gt;this&lt;/a&gt; is the best answer&amp;nbsp;now?&lt;/strong&gt;&lt;/p&gt;</summary></entry><entry><title>A Solution for DDoS</title><link href="//fattuba.com/a-solution-for-ddos" rel="alternate"></link><updated>2016-10-09T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2016-10-09:a-solution-for-ddos</id><summary type="html">&lt;p&gt;If you haven&amp;#8217;t heard the news yet, there was a &lt;a href="https://krebsonsecurity.com/2016/09/krebsonsecurity-hit-with-record-ddos/"&gt;record DDoS
attack&lt;/a&gt;
perpetrated against Brian Krebs last month.  &lt;a href="https://en.wikipedia.org/wiki/Denial-of-service_attack"&gt;Distributed denial of
service attacks&lt;/a&gt;
are nothing new.  But the particulars of attack changing.  In the
past, DDoS attacks used reflection or amplification techniques to
take a large botnet and compound their effective attack traffic.
The big deal with this new attack is that the botnet was &lt;em&gt;much&lt;/em&gt;
bigger than previous botnets.  So big, in fact that reflection
and amplification hacks weren&amp;#8217;t necessary.  The purported size is &lt;a href="http://www.theregister.co.uk/2016/09/26/brian_krebs_site_ddos_was_powered_by_hacked_internet_of_things_botnet/"&gt;1
million&lt;/a&gt;
devices.  And the botnet was comprised mainly of IoT devices such as
security&amp;nbsp;cameras.&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/botnet.jpg" alt="botnet" /&gt;
&lt;cite&gt;&amp;#8220;Botnet&amp;#8221; by &lt;a href="https://www.flickr.com/photos/tecnomovida/14792354835"&gt;tecnomovida&lt;/a&gt; is licensed under &lt;a href="https://creativecommons.org/licenses/by-nc-sa/2.0/"&gt;&lt;span class="caps"&gt;CC&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt;-&lt;span class="caps"&gt;NC&lt;/span&gt;-&lt;span class="caps"&gt;SA&lt;/span&gt;&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;That has all been well-covered by the tech press.  The question is,
how do we prevent or at least reduce these kinds of attacks?  As usual,
follow the&amp;nbsp;money!&lt;/p&gt;
&lt;p&gt;The real issue is one of economics and not technology.  These DDoS
attacks come from p0wned devices on users&amp;#8217; networks.  Right now the
service providers(e.g. Brian Krebs) pay per byte for their bandwidth
but users do not.  Most home and business internet connections are not
billed per byte.  So most users don&amp;#8217;t care how much traffic they&amp;nbsp;generate.&lt;/p&gt;
&lt;p&gt;Consequently since the users aren&amp;#8217;t paying for the traffic, there&amp;#8217;s
no economic incentive for them to keep their devices up-to-date with
security&amp;nbsp;patches.&lt;/p&gt;
&lt;p&gt;What if users&amp;#8217; internet was billed per byte?  If a user&amp;#8217;s device were
a part of a DDoS botnet, their monthly internet bill would increase due
to the increase in traffic coming from the hacked device.  The user would
then be more likely to try to find the rogue device and take it offline
or clean it.  Subsequently, users would start demanding that manufacturers
create more secure devices that are patched on a regular&amp;nbsp;basis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&amp;nbsp;2026-1-25&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Because the economic incentive is widely distributed among the device
owners relative to the benefit to the DDoS attacker, the plan above
probably won&amp;#8217;t work.  It&amp;#8217;s the computer security equivalent to a &lt;a href="https://en.wikipedia.org/wiki/Salami_slicing_tactics"&gt;salami
attack&lt;/a&gt;.&lt;/p&gt;</summary></entry><entry><title>IoT Platform Dominance</title><link href="//fattuba.com/iot-platform-dominance" rel="alternate"></link><updated>2016-05-13T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2016-05-13:iot-platform-dominance</id><summary type="html">&lt;p&gt;There&amp;#8217;s a lot of talk about the IoT platform, what that is exactly and
what companies will end up winning.  Well, I think it snuck up on us
without us realizing it.  There are really two levels of IoT&amp;nbsp;devices.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Super low-powered - These are typically powered by a coin cell
      battery and run code on&amp;nbsp;bare-metal.&lt;/li&gt;
&lt;li&gt;Higher powered - These are typically plugged in or Li-Ion powered
      and run&amp;nbsp;Linux.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The development kits and ecosystems around each of these device types
are the&amp;nbsp;platforms.&lt;/p&gt;
&lt;h2&gt;What Platforms Are&amp;nbsp;Winning?&lt;/h2&gt;
&lt;p&gt;Both of these device types already have dominant players, at least
in-terms of prototyping.  Arduino is the leader amongst the
low-powered devices while Raspberry Pi leads in the higher powered&amp;nbsp;area.&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/arduino_uno.jpg" alt="Picture of Arduino Uno" /&gt;
&lt;cite&gt;&amp;#8220;A hand-soldered Arduino&amp;#8221; by &lt;a href="http://www.flickr.com/people/51035707449@N01"&gt;Matt Biddulph&lt;/a&gt; is licensed under &lt;a href="https://creativecommons.org/licenses/by-sa/2.0/deed.en"&gt;&lt;span class="caps"&gt;CC&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt;-&lt;span class="caps"&gt;SA&lt;/span&gt; 2.0&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;h2&gt;Why Are These&amp;nbsp;Winning?&lt;/h2&gt;
&lt;p&gt;I think the reasons that these platforms lead are pretty&amp;nbsp;simple.&lt;/p&gt;
&lt;p&gt;First, I can buy them
&lt;a href="http://www.amazon.com/Raspberry-Pi-Model-512MB-Computer/dp/B00LPESRUK"&gt;on&lt;/a&gt;
&lt;a href="http://www.amazon.com/Arduino-UNO-Board-Module-ATmega328P/dp/B01A0MONA0"&gt;Amazon&lt;/a&gt;.
Easy access to these devices make them a great choice for prototyping.
You don&amp;#8217;t have to call a sales rep or navigate some vendor&amp;#8217;s horrible
estore to get&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;Second, most of the code I need is already available.  The communities for both
the &lt;a href="https://www.adafruit.com/category/105"&gt;Pi&lt;/a&gt; and &lt;a href="https://www.adafruit.com/category/17"&gt;Arduino&lt;/a&gt; are massive.  There are tons of howtos and tutorials with
code included that do everything from &lt;a href="https://learn.adafruit.com/getting-started-with-the-nrf8001-bluefruit-le-breakout"&gt;talk using Bluetooth from
a
Phone&lt;/a&gt; to &lt;a href="https://learn.adafruit.com/adafruit-2-8-pitft-capacitive-touch/"&gt;adding a touch screen&lt;/a&gt;.  The mind share around this stuff is really&amp;nbsp;insane.&lt;/p&gt;
&lt;p&gt;Finally, the hardware is open-ish.  One of the things to
consider when choosing a prototyping platform is how easily
can you move to production.  Arduino is legitimately &lt;a href="https://www.arduino.cc/en/Main/FAQ"&gt;open
hardware&lt;/a&gt;, so I can take
a duct-taped prototype to a &lt;span class="caps"&gt;PCB&lt;/span&gt; shop and they can quickly
and easily give me a custom &lt;span class="caps"&gt;PCB&lt;/span&gt; based on the prototype.
Most &lt;span class="caps"&gt;PCB&lt;/span&gt; shops have done Arduino-based boards before so it&amp;#8217;s
like a walk in the park.  Although the Raspberry Pi &lt;a href="https://www.raspberrypi.org/forums/viewtopic.php?t=55777&amp;amp;p=422729"&gt;isn&amp;#8217;t open
hardware&lt;/a&gt;
they are &lt;a href="http://arstechnica.com/information-technology/2014/04/raspberry-pi-gets-more-arduino-y-with-new-open-source-modular-hardware/"&gt;moving in that
direction&lt;/a&gt;.
Also, there are shops that specialize in &lt;a href="https://www.quora.com/How-can-I-move-a-Raspberry-Pi-IoT-prototype-to-production"&gt;taking Pi prototypes to
production&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/raspberry_pi.jpg" alt="Picture of Raspberry Pi" /&gt;
&lt;cite&gt;&amp;#8220;Raspberry Pi&amp;#8221; by &lt;a href="https://commons.wikimedia.org/wiki/File:Raspberry_Pi_Photo.jpg"&gt;cowjuice&lt;/a&gt; is licensed under &lt;a href="https://creativecommons.org/licenses/by-sa/3.0/deed.en"&gt;&lt;span class="caps"&gt;CC&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt;-&lt;span class="caps"&gt;SA&lt;/span&gt; 3.0&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;h2&gt;What Companies Win&amp;nbsp;Here?&lt;/h2&gt;
&lt;p&gt;Broadcom makes the SoC on the Raspberry Pi and Atmel makes the
micro controller on the Arduino so  I think those guys have an advantage in the
market moving forward.  Of course all this depends on my assumption that easy
prototyping translates into massive chip sales in the long run.  What do &lt;a href="mailto:tubaman@fattuba.com"&gt;you
think&lt;/a&gt;?&lt;/p&gt;</summary></entry><entry><title>IoT Usefulness</title><link href="//fattuba.com/iot-usefulness" rel="alternate"></link><updated>2016-04-06T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2016-04-06:iot-usefulness</id><summary type="html">&lt;p&gt;Ok so why haven&amp;#8217;t all my geek friends put smart lighting everywhere in their
houses?  I think it comes down to usefulness or bang for the&amp;nbsp;buck.&lt;/p&gt;
&lt;p&gt;For the purposes of this discussion, let&amp;#8217;s define IoT as connecting a
physical thing that hasn&amp;#8217;t traditionally been connected in the&amp;nbsp;past.&lt;/p&gt;
&lt;p&gt;Ok, so after talking with &lt;a href="http://tytusblog.blogspot.com/"&gt;Harlan&lt;/a&gt;
about IoT in general(he&amp;#8217;s &lt;a href="https://events.bizzabo.com/datapopupaustin"&gt;speaking next
Wednesday&lt;/a&gt; on it), we came
up with the following four categories of IoT&amp;nbsp;usefulness:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;saves me&amp;nbsp;time&lt;/li&gt;
&lt;li&gt;saves me&amp;nbsp;money&lt;/li&gt;
&lt;li&gt;entertains&amp;nbsp;me&lt;/li&gt;
&lt;li&gt;makes me&amp;nbsp;healthier&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/internet-of-things.jpg" alt="The Internet of Things" /&gt;
&lt;cite&gt;&amp;#8220;The Internet of Things&amp;#8221; by &lt;a href="https://www.flickr.com/photos/wilgengebroed/"&gt;wilgengebroed&lt;/a&gt; is licensed under &lt;a href="https://creativecommons.org/licenses/by/2.0/deed.en"&gt;&lt;span class="caps"&gt;CC&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt; 2.0&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;p&gt;An IoT product must provide benefit in one or more of the four categories
above otherwise, it won&amp;#8217;t sell.  Here are some&amp;nbsp;examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nest thermostat - saves me&amp;nbsp;money&lt;/li&gt;
&lt;li&gt;Smart prompt kitchen thermometer - saves me&amp;nbsp;time&lt;/li&gt;
&lt;li&gt;Tricella pill box- makes me&amp;nbsp;healthier&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I am specifically excluding media devices from the IoT space(&lt;span class="caps"&gt;TV&lt;/span&gt;, radio,
speakers) since those have been traditionally connected for a while&amp;nbsp;now.&lt;/p&gt;
&lt;p&gt;Aside: Amazon Echo isn&amp;#8217;t an IoT device, but instead a new interface(voice
instead of screen) to any services(IoT or non-IoT) that a screen would
traditionally control.  Does combining voice interface with some IoT
product suddenly make that IoT product useful in one of the four
categories where it wasn&amp;#8217;t&amp;nbsp;before?&lt;/p&gt;
&lt;p&gt;Regarding the unified dashboard idea, how does pulling together the
disparate sources of IoT data make those IoT products hit the threshold
in one of the&amp;nbsp;categories?&lt;/p&gt;
&lt;p&gt;Anyway, I think a framework like this is useful for discussion.  Is the
framework presented the right way of thinking about&amp;nbsp;IoT? &lt;/p&gt;</summary></entry><entry><title>Android is GMS</title><link href="//fattuba.com/android-is-gms" rel="alternate"></link><updated>2015-11-07T00:00:00-06:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2015-11-07:android-is-gms</id><summary type="html">&lt;p&gt;Well friends.  I think it&amp;#8217;s time to call it like it is.  Android &lt;em&gt;is&lt;/em&gt;
Google Mobile Services.  You see, Google would like us to think that
Android is an open source mobile operating system that anybody can use
and extend for free to do whatever they want.  Here&amp;#8217;s the hitch: the
Google Play Store isn&amp;#8217;t part of Android open source.  The Play Store(along
with other Google apps(YouTube, Maps, GMail, etc)is part of what Google
calls &lt;a href="https://source.android.com/compatibility/"&gt;Google Mobile Services&lt;/a&gt;
or &lt;span class="caps"&gt;GMS&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;And &lt;span class="caps"&gt;GMS&lt;/span&gt; is &lt;em&gt;not&lt;/em&gt; open source.  Use of &lt;span class="caps"&gt;GMS&lt;/span&gt; requires a separate license
from Google.  And while that license &lt;a href="http://9to5google.com/2014/01/23/google-we-do-not-charge-licensing-fees-for-androids-google-mobile-services/"&gt;doesn&amp;#8217;t cost
anything&lt;/a&gt;,
it&amp;#8217;s almost impossible to obtain for most&amp;nbsp;developers.&lt;/p&gt;
&lt;p&gt;You see, &lt;a href="http://www.keyingredient.com/kitchensnap/featured/key-ingredient-recipe-reader-hd/"&gt;we&amp;#8217;ve built an Android
device&lt;/a&gt;
this year just in time for Christmas.  We really worked hard to put
together the best recipe reader we&amp;#8217;ve ever done(fastest, crispest
screen,&amp;nbsp;etc).&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/ki_rrhd.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The only problem: no Google Mobile Services.  We couldn&amp;#8217;t even
get Google to talk to us about &lt;span class="caps"&gt;GMS&lt;/span&gt; licensing.  Here was the response that we&amp;nbsp;got:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello&amp;nbsp;Ryan,&lt;/p&gt;
&lt;p&gt;Thanks for your&amp;nbsp;interest.&lt;/p&gt;
&lt;p&gt;We are very swamped for direct license and ask partners work with
our authorized ODMs.  Attached the &lt;span class="caps"&gt;ODM&lt;/span&gt; list. If you can ask your
manufacturing to either of them, they can bundle Google applications
per our&amp;nbsp;approval.&lt;/p&gt;
&lt;p&gt;Regards&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ok, we thought, we&amp;#8217;ll just build our new reader using one of the parter
ODMs.  The problem is that the hardware available from those partners is
just average!  We are committed to shipping the fastest clearest reader
possible and we weren&amp;#8217;t willing to sacrifice speed or screen&amp;nbsp;clarity.&lt;/p&gt;
&lt;p&gt;So what does that mean?  That means we have a &lt;em&gt;killer&lt;/em&gt; reader that uses the
&lt;a href="https://www.amazon.com/gp/feature.html?ie=UTF8&amp;amp;docId=1003016361&amp;amp;ref_=mas_surl_undrgrnd"&gt;Amazon Underground App
Store&lt;/a&gt; instead of Google Play.  Now the Amazon Store does have some advantages over the Play Store: free apps that otherwise cost money in Google Play, easy access to Amazon video, etc.  But is that enough?  How will the market react?  I think once the reviews start coming in we&amp;#8217;ll&amp;nbsp;know.&lt;/p&gt;
&lt;p&gt;What do you think?  If you can&amp;#8217;t download apps from the Play Store,
is it still Android?  I&amp;#8217;d love &lt;a href="mailto:tubaman@fattuba.com"&gt;to discuss&lt;/a&gt;.&lt;/p&gt;</summary></entry><entry><title>The Future of Mobile Ads</title><link href="//fattuba.com/the-future-of-mobile-ads" rel="alternate"></link><updated>2015-10-07T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2015-10-07:the-future-of-mobile-ads</id><summary type="html">&lt;p&gt;Mobile advertising is at a crossroads.  &lt;a href="http://9to5mac.com/2015/06/10/block-ads-ios-9-safari-iphone/"&gt;Apple has just enabled ad blockers&lt;/a&gt; in iOS.
And users are realizing how much &lt;a href="https://medium.com/@robleathern/carriers-are-making-more-from-mobile-ads-than-publishers-are-d5d3c0827b39"&gt;mobile data is being consumed by
ads&lt;/a&gt;.  Since Apple has little vested interest in web ads and a huge interest in building the best product so that users will shell out a premium for them, ad blocking makes sense in&amp;nbsp;iOS.&lt;/p&gt;
&lt;p&gt;Mobile ads can make pages load up to 3 times slower and consume 6 times the
bandwidth of the same page &lt;em&gt;without ads&lt;/em&gt;.  Since the ads don&amp;#8217;t have any
immediate value for the user, the web is a better experience without the ads.
So, from a user&amp;#8217;s perspective, &amp;#8220;no ads&amp;#8221; is&amp;nbsp;better.&lt;/p&gt;
&lt;p&gt;Now for the other perspective.  I work for a
&lt;a href="http://www.keyingredient.com"&gt;company&lt;/a&gt; that makes part of their revenue
through mobile advertising.  Like most sites in the U.S., a huge percentage
of our mobile traffic comes from iOS.  Enough has been said about ad blockers
causing the &lt;a href="http://www.theguardian.com/commentisfree/2015/sep/27/ad-blocking-herald-end-of-free-internet-ios9-apple"&gt;potential downfall of the free web&lt;/a&gt;.
To summarize, the long-term value of ads for the user is that the content
can stay free.  But instead of rehashing all that, let&amp;#8217;s discuss possible
solutions.  To quickly re-summarize the problem, mobile&amp;nbsp;ads:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;cost users real dollars in the form of mobile&amp;nbsp;data&lt;/li&gt;
&lt;li&gt;don&amp;#8217;t provide any near-term value for&amp;nbsp;users&lt;/li&gt;
&lt;li&gt;slow down the web and make for a worse&amp;nbsp;experience&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The ultimate solution fixes all three of these.  So, let&amp;#8217;s serve ads&amp;nbsp;that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;don&amp;#8217;t consume much mobile&amp;nbsp;data&lt;/li&gt;
&lt;li&gt;provide near-term value for&amp;nbsp;users&lt;/li&gt;
&lt;li&gt;load quickly so that the web is still&amp;nbsp;snappy&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The obvious solution is to do advertorial content instead of the usual
display ads.  This is content that is potentially valuable in the
near-term for users because it provides useful information while at
the same time pitching a product or service.  It also doesn&amp;#8217;t consume
any more mobile data than a normal non-advertorial piece of content.
And since there don&amp;#8217;t have to be 3rd-party ad networks involved, no
extra code needs to be loaded so the site stays snappy.  The downside
to advertorials is that it takes real work to create them.  Someone has
to write the&amp;nbsp;content.&lt;/p&gt;
&lt;p&gt;Got other ideas?  I&amp;#8217;d &lt;a href="mailto:ryan@fattuba.com"&gt;love to discuss&lt;/a&gt;.&lt;/p&gt;</summary></entry><entry><title>Agile Hardware</title><link href="//fattuba.com/agile-hardware" rel="alternate"></link><updated>2015-09-02T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2015-09-02:agile-hardware</id><summary type="html">&lt;p&gt;At Key Ingredient, we&amp;#8217;re pretty heavily invested in Agile, Scrum in particular.
This year we &lt;em&gt;really&lt;/em&gt; wanted to try to ship some hardware in an Agile fashion.
So after hiring a team and discussing, we decided that Kanban was right for the
hardware projects.  The first hardware project we tried was our &lt;a href="http://www.keyingredient.com/smartprompt-pan/"&gt;little smart pan&lt;/a&gt;.  The idea is that we&amp;#8217;d ship a new prototype every two weeks to &lt;a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food"&gt;dogfood&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By using off-the-shelf parts, we were able to do the first prototype in 14 days. 
&lt;img src="//fattuba.com/images/sp5.jpg" alt="SmartPan 5" /&gt;&lt;/p&gt;
&lt;p&gt;Our Mark I prototype(I&amp;#8217;m a Marvel Fan), was built using Raspberry Pi B plus some
other stuff from &lt;a href="http://adafruit.com"&gt;Adafruit&lt;/a&gt;.  It just did simple
temperature measurement and alerts.  Mark &lt;span class="caps"&gt;II&lt;/span&gt; was just a cleaned up
version of Mark I.  We did a better job of hiding the&amp;nbsp;wires.&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/sp4.jpg" alt="SmartPan 4" /&gt;&lt;/p&gt;
&lt;p&gt;Over time, we added additional features including boiling and burning&amp;nbsp;detection.&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/sp3.jpg" alt="SmartPan 3" /&gt;&lt;/p&gt;
&lt;p&gt;Mark V even included a built-in&amp;nbsp;touchscreen.&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/sp2.jpg" alt="SmartPan 2" /&gt;&lt;/p&gt;
&lt;p&gt;Overall, the project has been super fun and most importantly has allowed us to
learn a ton about what it takes to make a connected kitchen&amp;nbsp;product.&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/sp1.jpg" alt="SmartPan 1" /&gt;&lt;/p&gt;</summary></entry><entry><title>Typing</title><link href="//fattuba.com/typing" rel="alternate"></link><updated>2015-04-27T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2015-04-27:typing</id><summary type="html">&lt;p&gt;I&amp;#8217;m shocked that I need to actually say this.  If you work with
computers(sysadmin, programming, whatever), learn to touch type!
I had a 15 minute conversation with a college student about whether he
should go into network administration or security auditing.  After we
finished talking, he turned around to his keyboard and started &lt;em&gt;two
finger typing&lt;/em&gt;!  I&amp;#8217;m talking hunting and pecking&amp;nbsp;folks.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s like a carpenter&amp;#8217;s apprentice repeatedly hitting himself in the
thumb with his&amp;nbsp;hammer.&lt;/p&gt;
&lt;p&gt;If you buy the whole thing about software engineers being craftsmen, then your
keyboard is a tool like your text editor, compiler, debugger, etc.  You need to
know how to use it.  And there&amp;#8217;s really no excuse.  There&amp;#8217;s about
&lt;a href="https://www.google.com/search?q=typing+tutor"&gt;a million&lt;/a&gt; free typing
tutor sites.  Pick one and&amp;nbsp;start.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s even a &lt;a href="http://en.wikipedia.org/wiki/The_Typing_of_the_Dead"&gt;version of House of The
Dead&lt;/a&gt; where you kill
zombies by typing Japanese words floating above their heads instead of
shooting&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/typing_of_the_dead.jpg" alt="Typing of the Dead" /&gt;&lt;/p&gt;
&lt;p&gt;So you really have no excuse&amp;nbsp;:)&lt;/p&gt;</summary></entry><entry><title>Temptation Bundling</title><link href="//fattuba.com/temptation-bundling" rel="alternate"></link><updated>2015-03-18T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2015-03-18:temptation-bundling</id><summary type="html">&lt;p&gt;Freakonomics Radio has a great &lt;a href="http://freakonomics.com/2015/03/13/when-willpower-isnt-enough-a-new-freakonomics-radio-podcast/"&gt;episode on temptation
bundling&lt;/a&gt;.
Temptation bundling is the idea that if you have a chore that you hate to do,
you should &amp;#8220;bundle&amp;#8221; it with something that you like.  The classic example
is watching your favorite &lt;span class="caps"&gt;TV&lt;/span&gt; show only when running on the treadmill.
The advantage of temptation bundling over some other similar techniques
is that it makes &lt;em&gt;both&lt;/em&gt; activites better.  You don&amp;#8217;t feel guilty that you&amp;#8217;re
wasting time watching &lt;span class="caps"&gt;TV&lt;/span&gt; and you actual enjoy running on the&amp;nbsp;treadmill!&lt;/p&gt;</summary></entry><entry><title>Tasker Is the Killer Android App</title><link href="//fattuba.com/tasker-is-the-killer-android-app" rel="alternate"></link><updated>2015-02-18T00:00:00-06:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2015-02-18:tasker-is-the-killer-android-app</id><summary type="html">&lt;p&gt;As far as I&amp;#8217;m concerned, &lt;em&gt;the&lt;/em&gt; killer app for Android, is
&lt;a href="https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm&amp;amp;hl=en"&gt;Tasker&lt;/a&gt;.
I don&amp;#8217;t think there&amp;#8217;s anything like it for iOS.  Tasker allows you to
associate various events with actions(sort of like &lt;span class="caps"&gt;IFTTT&lt;/span&gt;).  So if I&amp;#8217;m at work,
tasker sets my phone to vibrate.  &amp;#8220;At Work&amp;#8221; is defined as:  Wifi is associated
with the KeyArt access point(our &lt;span class="caps"&gt;AP&lt;/span&gt; at work).  Here are some example of&amp;nbsp;events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;wifi related - a certain access&amp;nbsp;point&lt;/li&gt;
&lt;li&gt;bluetooth related - connected to a certain&amp;nbsp;device&lt;/li&gt;
&lt;li&gt;time related - within a time&amp;nbsp;range&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;GPS&lt;/span&gt; related - within a radius of certain&amp;nbsp;coordinates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;#8230;and example&amp;nbsp;actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;start an&amp;nbsp;app&lt;/li&gt;
&lt;li&gt;stop an&amp;nbsp;app&lt;/li&gt;
&lt;li&gt;adjust&amp;nbsp;volume&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So here&amp;#8217;s a more complicated and incredibly useful example.  When I&amp;#8217;m leaving
work, I automatically start Waze and start navigating home.  &amp;#8220;Leaving Work&amp;#8221; is
defined like&amp;nbsp;this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If I&amp;#8217;m connected to my car bluetooth&amp;nbsp;and&amp;#8230;&lt;/li&gt;
&lt;li&gt;I&amp;#8217;m within 500 ft of my work&amp;nbsp;and&amp;#8230;&lt;/li&gt;
&lt;li&gt;It&amp;#8217;s between 4 pm and 9 pm in the&amp;nbsp;evening&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Starting Waze and automatically navigating home is just an Android
&lt;a href="http://developer.android.com/reference/android/content/Intent.html"&gt;intent&lt;/a&gt; and
Tasker can send those as&amp;nbsp;actions. &lt;/p&gt;
&lt;p&gt;Now that I&amp;#8217;ve automated much of the routine interaction with my phone
it would be hard to go&amp;nbsp;back.&lt;/p&gt;</summary></entry><entry><title>My Dev Setup</title><link href="//fattuba.com/my-dev-setup" rel="alternate"></link><updated>2014-12-08T00:00:00-06:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-12-08:my-dev-setup</id><summary type="html">&lt;p&gt;My work setup right now is a &lt;a href="http://shop.lenovo.com/us/en/laptops/thinkpad/w-series/w530/"&gt;Thinkpad
W530&lt;/a&gt;
running &lt;a href="https://www.debian.org/releases/wheezy/"&gt;Debian Wheezy&lt;/a&gt;.
I&amp;#8217;ve been pretty happy with it.  I&amp;#8217;m using chroots with Debian Unstable
and the latest Ubuntu to install any newer userspace programs I need.
I&amp;#8217;m using VirtualBox to run Windows 7 and Microsoft Office(under&amp;nbsp;duress).&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/thinkpad.jpg" alt="Thinkpad running Debian Wheezy"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;p&gt;The machine is pretty reliable.  My current uptime is 47&amp;nbsp;days.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;tubaman&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;cuisine&lt;/span&gt;&lt;span class="o"&gt;:~&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;uptime&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt; &lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.57&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.63&lt;/span&gt;
&lt;span class="n"&gt;tubaman&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;cuisine&lt;/span&gt;&lt;span class="o"&gt;:~&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I have had a few issues&amp;nbsp;though.&lt;/p&gt;
&lt;h2&gt;Hardware&amp;nbsp;Issues&lt;/h2&gt;
&lt;p&gt;Overall the W530 is a good machine.  However, the ribbon cable that
connects the web cam keeps coming loose.  That means that the web cam
just stops working.  I guess the repeated opening and closing of the
laptop causes the cable to work free.  To fix it I had our resident
hardware hacker crack it open and hot glue the cable into place.  Disappointing
but nonetheless&amp;nbsp;fixable.&lt;/p&gt;
&lt;p&gt;The other issue I have with the hardware is that the plastic case has
developed a crack in the corner.  I had a similar problem with my old
Thinkpad T40p.  This is where I get a little jealous of my Mac friends
with their all-aluminum&amp;nbsp;case.&lt;/p&gt;
&lt;h2&gt;Software&amp;nbsp;Issues&lt;/h2&gt;
&lt;p&gt;The W530 comes with an on-board Intel graphics chip &lt;em&gt;plus&lt;/em&gt;
a discrete NVidia graphics card.  The NVidia guys call this
&lt;a href="http://www.nvidia.com/object/optimus_technology.html"&gt;Optimus&lt;/a&gt;.
It allows you to run on the lower-power on-board graphics until you&amp;#8217;re
playing a game or something.  That&amp;#8217;s when it switches on the discrete
graphics.  The drivers for Linux are not quite as seamless as the Windows
drivers yet.  However the Bumblebee Project has a tool called bbswitch
that works pretty well.  I use that to enable the external monitor.
Oh yeah, did I mention that the external monitor port is connected to
the discrete graphics&amp;nbsp;chip?&lt;/p&gt;</summary></entry><entry><title>VARK and UNIX</title><link href="//fattuba.com/vark-and-unix" rel="alternate"></link><updated>2014-11-27T00:00:00-06:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-11-27:vark-and-unix</id><summary type="html">&lt;p&gt;On the recommendation of our product owner, everyone on the team is
taking the &lt;a href="http://vark-learn.com/the-vark-questionnaire/"&gt;&lt;span class="caps"&gt;VARK&lt;/span&gt; quiz&lt;/a&gt;.
It&amp;#8217;s designed to tell you what your learning style is.  The options are
visual, aural, reading, and kinesthetic.  Here are my&amp;nbsp;scores:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visual -&amp;nbsp;0&lt;/li&gt;
&lt;li&gt;Aural -&amp;nbsp;5&lt;/li&gt;
&lt;li&gt;Read/Write -&amp;nbsp;4&lt;/li&gt;
&lt;li&gt;Kinesthetic -&amp;nbsp;7&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wonder if those of us who gravitate towards the command line instead
of pretty UIs have similar&amp;nbsp;scores?&lt;/p&gt;</summary></entry><entry><title>Android Record and Playback</title><link href="//fattuba.com/android-record-and-playback" rel="alternate"></link><updated>2014-10-24T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-10-24:android-record-and-playback</id><summary type="html">&lt;p&gt;There are a
&lt;a href="http://android.stackexchange.com/questions/45121/is-there-a-way-to-record-and-playback-a-set-of-touches"&gt;bunch&lt;/a&gt;
&lt;a href="http://android.stackexchange.com/questions/13992/how-can-i-record-touches"&gt;of&lt;/a&gt;
suggestions online about how to record a series of touchscreen touches
and play them back.  None of them really work for me though.  I&amp;#8217;m looking
for something that doesn&amp;#8217;t require another app and optimally would work
over &lt;span class="caps"&gt;USB&lt;/span&gt; debugging with adb.  So I&amp;#8217;ve hacked something together myself.  I
started out by just capturing all the events from my touchscreen like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;getevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The events look like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="mo"&gt;0003&lt;/span&gt; &lt;span class="mo"&gt;003&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="mf"&gt;0000009f&lt;/span&gt;
&lt;span class="mo"&gt;0003&lt;/span&gt; &lt;span class="mo"&gt;003&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="mo"&gt;00000000&lt;/span&gt;
&lt;span class="mo"&gt;0003&lt;/span&gt; &lt;span class="mo"&gt;0035&lt;/span&gt; &lt;span class="mo"&gt;00000142&lt;/span&gt;
&lt;span class="mo"&gt;0003&lt;/span&gt; &lt;span class="mo"&gt;0036&lt;/span&gt; &lt;span class="mo"&gt;000001&lt;/span&gt;&lt;span class="mi"&gt;89&lt;/span&gt;
&lt;span class="mo"&gt;0000&lt;/span&gt; &lt;span class="mo"&gt;0000&lt;/span&gt; &lt;span class="mo"&gt;00000000&lt;/span&gt;
&lt;span class="mo"&gt;0003&lt;/span&gt; &lt;span class="mo"&gt;003&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;ffffffff&lt;/span&gt;
&lt;span class="mo"&gt;0000&lt;/span&gt; &lt;span class="mo"&gt;0000&lt;/span&gt; &lt;span class="mo"&gt;00000000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Those columns are the event type, code and data, all in hex.  I used a little python script to reformat them into sendevent calls that look like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt; &lt;span class="mi"&gt;159&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt; &lt;span class="mi"&gt;322&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt; &lt;span class="mi"&gt;393&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt; &lt;span class="mi"&gt;4294967295&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That works pretty well but doesn&amp;#8217;t take into account the delay between each
event so I modified my event capture to prepend a&amp;nbsp;timestamp:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;getevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;%Y-%m-%d %H:%M:%.S&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I modified my python script to add sleeps in between the sendevent&amp;nbsp;calls:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt; &lt;span class="mi"&gt;159&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt; &lt;span class="mi"&gt;322&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt; &lt;span class="mi"&gt;393&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;sleep&lt;/span&gt; &lt;span class="mf"&gt;0.041&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt; &lt;span class="mi"&gt;4294967295&lt;/span&gt;
&lt;span class="n"&gt;adb&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;sendevent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;event1&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now when I execute that I get an exact playback(or pretty close)!  Here&amp;#8217;s the final record&amp;nbsp;script:&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2
3&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

adb shell getevent /dev/input/event1 | ts &lt;span class="s2"&gt;&amp;quot;%Y-%m-%d %H:%M:%.S&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;&amp;#8230;and the python script that converts the output of the record script to
something that can get played&amp;nbsp;back:&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;

&lt;span class="n"&gt;line_re&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;quot;^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d+) (\S+) (\S+) (\S+)$&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;event_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;
&lt;span class="n"&gt;last_ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event_log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line_re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ecode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt; %H:%M:%S.&lt;/span&gt;&lt;span class="si"&gt;%f&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;etype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ecode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ecode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;last_ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;last_ts&lt;/span&gt;
        &lt;span class="n"&gt;sleeptime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%.3f&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sleeptime&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;sleep &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;sleeptime&lt;/span&gt;
    &lt;span class="n"&gt;last_ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;adb shell sendevent /dev/input/event1 &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ecode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;So now, to record I&amp;nbsp;do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&amp;#8230;and to playback I&amp;nbsp;do&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;convert_event_log_to_sendevents&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bash&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</summary></entry><entry><title>Bit by the POODLE</title><link href="//fattuba.com/bit-by-the-poodle" rel="alternate"></link><updated>2014-10-22T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-10-22:bit-by-the-poodle</id><summary type="html">&lt;p&gt;Well, the internet was all abuzz about
&lt;a href="https://www.us-cert.gov/ncas/alerts/TA14-290A"&gt;&lt;span class="caps"&gt;POODLE&lt;/span&gt;&lt;/a&gt; and it finally bit me.
I use selenium with phantomjs to do some &lt;a href="//fattuba.com/state-of-the-art-web-scraping"&gt;web
scraping&lt;/a&gt;.  One of the sites I scrap
uses &lt;span class="caps"&gt;SSL&lt;/span&gt; and they removed SSLv3 because of the &lt;span class="caps"&gt;POODLE&lt;/span&gt; vulnerability.  When
loading a page with phantomjs, it would not load but instead stay on the
&amp;#8220;about:blank&amp;#8221; page.  All the Googles say that I should just set
&lt;code&gt;--ignore-ssl-errors&lt;/code&gt; but that didn&amp;#8217;t help.  I realized that it was probably an
&lt;span class="caps"&gt;SSL&lt;/span&gt; protocol issue when I saw that phantomjs uses &lt;a href="http://phantomjs.org/api/command-line.html"&gt;only sslv3 by
default&lt;/a&gt;.  You have to tell
phantomjs to use other versions like this: &lt;code&gt;--ssl-protocol=any&lt;/code&gt;.&lt;/p&gt;</summary></entry><entry><title>State of the Art Web Scraping</title><link href="//fattuba.com/state-of-the-art-web-scraping" rel="alternate"></link><updated>2014-09-27T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-09-27:state-of-the-art-web-scraping</id><summary type="html">&lt;p&gt;My go-to toolkit for web scraping is python
&lt;a href="http://python-requests.org"&gt;requests&lt;/a&gt; and
&lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup4&lt;/a&gt;.
In combination, they provide a nice, clean &lt;span class="caps"&gt;API&lt;/span&gt; to interact with web sites
and parse content.  However recently I&amp;#8217;ve had a tough time logging into
some of the more complicated web sites(&lt;em&gt;ahem&lt;/em&gt; citibank).  The problem
is that citibank uses javascript to calculate tokens on the client that
are then passed to the server for checking.  If the tokens don&amp;#8217;t match,
the login fails.  I have to replicate these calculations in python for
my scraper to be able to login.  What a&amp;nbsp;pain!&lt;/p&gt;
&lt;p&gt;There are tools that are great for this kind of thing.  I like
&lt;a href="http://www.seleniumhq.org/"&gt;selenium&lt;/a&gt; using the phantomjs webdriver.
The problem is that while selenium is great for complicated logins,
it&amp;#8217;s terrible at things like downloading files or &lt;span class="caps"&gt;XHR&lt;/span&gt; &lt;span class="caps"&gt;JSON&lt;/span&gt;&amp;nbsp;data.&lt;/p&gt;
&lt;p&gt;I need a way to glue requests together with selenium so I can use
the right tool for the right job:  selenium for login and requests
for everything else.  Cookie copying to the rescue!  I use selenium to
login, then copy the cookies to requests for everything else.  Here&amp;#8217;s the
snippet to copy the&amp;nbsp;cookies:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;copy_cookies_to_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;Copy cookies from selenium webdriver to requests.Session&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;cookies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_cookies&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cookie&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now I can use the right tool for the right&amp;nbsp;job.&lt;/p&gt;</summary></entry><entry><title>Debugging Encrypted Web</title><link href="//fattuba.com/debugging-encrypted-web" rel="alternate"></link><updated>2014-09-26T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-09-26:debugging-encrypted-web</id><summary type="html">&lt;p&gt;Debugging encrypted web traffic can be challenging.  You have
Chrome Developer Tools but I find that being able to export the data
and browse through it in other ways can be easier.  I usually use
&lt;a href="https://www.wireshark.org/"&gt;wireshark&lt;/a&gt; to browse cleartext web traffic.
Now I can use it for encrypted &lt;span class="caps"&gt;HTTP&lt;/span&gt; traffic too.  The key is to use
&lt;a href="http://mitmproxy.org/"&gt;mitmproxy&lt;/a&gt; along with wireshark&amp;#8217;s built-in
&lt;span class="caps"&gt;SSL&lt;/span&gt; decryption.  Here are the steps to get it&amp;nbsp;working:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install mitmproxy and get it up and running (We&amp;#8217;ll use port 8119
      throughout the example&amp;nbsp;here)&lt;/li&gt;
&lt;li&gt;Convert the mitmproxy private key you generated into &lt;span class="caps"&gt;PEM&lt;/span&gt; format&lt;ul&gt;
&lt;li&gt;&lt;code&gt;openssl rsa -in mitmproxy-ca.pem -outform PEM -out mitmproxy-ca.key&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Setup wireshark to use the mitmproxy-ca.key to decrypt traffic
      &lt;img
      src="//fattuba.com/images/configure_mitmproxy_private_key_in_wireshark.png"/&gt;&lt;/li&gt;
&lt;li&gt;Start mitmproxy on port 8119 &lt;ul&gt;
&lt;li&gt;&lt;code&gt;mitmproxy -p 8119&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Start wireshark capturing on the lo interface and filter on port 8119&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wireshark -i lo -f "port 8119" -k&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Analyze -&amp;gt; Decode As&amp;#8230; -&amp;gt; &lt;span class="caps"&gt;SSL&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You should see the &lt;span class="caps"&gt;HTTPS&lt;/span&gt; traffic in wireshark being automatically decrypted so
you can see the layers: &lt;span class="caps"&gt;TCP&lt;/span&gt;, &lt;span class="caps"&gt;SSL&lt;/span&gt;, &lt;span class="caps"&gt;HTTP&lt;/span&gt;.&lt;/p&gt;</summary></entry><entry><title>More Internets!</title><link href="//fattuba.com/more-internets" rel="alternate"></link><updated>2014-08-23T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-08-23:more-internets</id><summary type="html">&lt;p&gt;In Austin, there are basically two ISPs, Time Warner and &lt;span class="caps"&gt;AT&lt;/span&gt;&amp;amp;T.  That&amp;#8217;s not
really enough competition to be good for consumers but that discussion
is for another time.  The lesser of these two evils is Time Warner so
that&amp;#8217;s my &lt;span class="caps"&gt;ISP&lt;/span&gt;.  The other week Time Warner sent me&amp;nbsp;this:&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/tw_1.jpg" alt="Time Warner Offer Page 1"&gt;&lt;/img&gt;
&lt;img src="//fattuba.com/images/tw_2.jpg" alt="Time Warner Offer Page 2"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;p&gt;Triple the speed for no additional cost Sounds great, right?  Maybe it&amp;#8217;s
not as great as it sounds.  You see, the Internet is not a dedicated
line between your house and Netflix.  It&amp;#8217;s a &lt;a href="http://en.wikipedia.org/wiki/Packet_switching"&gt;packet
switched&lt;/a&gt; network with
many varying hops between you and &lt;a href="http://www.imdb.com/title/tt1856010/"&gt;House of
Cards&lt;/a&gt;.  So while speedtest.net
may show you rocketing across the&amp;nbsp;internet:&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/temple_speedtest_results.png" alt="Speedtest.net results to Temple, TX"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8230;Netflix still may be limited to 720p instead of the glorious 1080p
that your connection warrants.  That&amp;#8217;s because major backbone providers
connect to each other through peering points and if there&amp;#8217;s &lt;a href="http://mashable.com/2014/07/14/netflix-verizon-still-slow/"&gt;congestion
at the peering point&lt;/a&gt; between Time Warner and Netflix&amp;#8217;s &lt;span class="caps"&gt;ISP&lt;/span&gt;, then &lt;a href="http://www.imdb.com/name/nm0000228/"&gt;Kevin
Spacey&lt;/a&gt; doesn&amp;#8217;t look as crytal
clear as he&amp;nbsp;could.&lt;/p&gt;
&lt;p&gt;The solution is to rant about these things in public until the ISPs play nice
and make their peering points&amp;nbsp;fatter.&lt;/p&gt;</summary></entry><entry><title>People Not Tech</title><link href="//fattuba.com/people-not-tech" rel="alternate"></link><updated>2014-08-22T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-08-22:people-not-tech</id><summary type="html">&lt;p&gt;My new gig has forced me to grow as an individual and a software developer.
When I interviewed I let them know that I had never managed a team before.
They said that I &amp;#8220;would be fine.&amp;#8221;  Needless to say it&amp;#8217;s been a bumpy ride.
Over the last 9 months I&amp;#8217;ve come to realize that software in general
is less about tech and more about people.  I think it partially stems
from the fact that software development is more art than science.
And software engineers are closer to artists than&amp;nbsp;engineers.&lt;/p&gt;</summary></entry><entry><title>Tale of 1000 Androids</title><link href="//fattuba.com/tale-of-1000-androids" rel="alternate"></link><updated>2014-07-26T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-07-26:tale-of-1000-androids</id><summary type="html">&lt;p&gt;Two years ago my trusty Motorola Atrix 4G fell to the pavement(ahem I dropped
it) and the camera stopped working.  So I went down to the &lt;span class="caps"&gt;AT&lt;/span&gt;&amp;amp;T store
since it was time for an upgrade anyway, and bought a Galaxy S3.  I ran
with the stock firmware for about 30 seconds before rooting, unlocking
the bootloader and flashing CyanogenMod.  I really like stock Android
in place of Samsung Touchwiz.  I&amp;#8217;ve been running happily with my hacked
S3 for 2 years.  During that time, my &lt;a href="//fattuba.com/snowday"&gt;daughter&lt;/a&gt;
had been using my old Atrix running CyanogenMod.  The Atrix finally gave
up the ghost so naturally&amp;#8230; she gets my S3 and I get a new&amp;nbsp;phone!&lt;/p&gt;
&lt;p&gt;I went down to the &lt;span class="caps"&gt;AT&lt;/span&gt;&amp;amp;T store to do the upgrade dance and bought an S4.  The S5
is so new that it doesn&amp;#8217;t have good CyanogenMod support yet.  After monkeying
with the S4 for about 24 hours, I realize that &lt;span class="caps"&gt;AT&lt;/span&gt;&amp;amp;T and Samsung have locked the
bootloader using mathematically strong, well implemented crypto.  So I took it
back.  No matter how fantastic the phone is, I really want to be able to switch
out the &lt;span class="caps"&gt;ROM&lt;/span&gt;.  I traded it for a Moto X.  I&amp;#8217;ve heard good things including it&amp;#8217;s
low power mode.  Plus there&amp;#8217;s a page on the Motorola site specifically for
requesting a bootloader unlock code!  That is, except for &lt;span class="caps"&gt;AT&lt;/span&gt;&amp;amp;T phones.
Whoops.  Motorola will unlock other Moto X&amp;#8217;s but not &lt;span class="caps"&gt;AT&lt;/span&gt;&amp;amp;T&amp;#8217;s.  I can only
assume this is due to some contract between the&amp;nbsp;companies.&lt;/p&gt;
&lt;p&gt;At this point I&amp;#8217;m pretty fed up.  After listening to my rant, the guys at the
office said, &amp;#8220;Dude, just buy a Nexus 5&amp;#8221;.  A few clicks and I had purchased
a Nexus 5 from Google&amp;#8217;s website.  Now I&amp;#8217;m on my way to return the Moto
X to &lt;span class="caps"&gt;AT&lt;/span&gt;&amp;amp;T and let them know that good hardware is not enough.  I need
the freedom to run whatever I&amp;nbsp;want.&lt;/p&gt;</summary></entry><entry><title>IoT Standards Groups</title><link href="//fattuba.com/iot-standards-groups" rel="alternate"></link><updated>2014-07-21T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-07-21:iot-standards-groups</id><summary type="html">&lt;p&gt;Apparently there are now 3 standards bodies for the Internet of
Things, &lt;a href="https://allseenalliance.org/"&gt;AllSeen Alliance&lt;/a&gt;, &lt;a href="http://www.openinterconnect.org/"&gt;Open
Interconnect Consortium&lt;/a&gt;, and &lt;a href="http://threadgroup.org/"&gt;Thread
Group&lt;/a&gt;.  The only folks talking about protocol
is Thread Group.  That&amp;#8217;s where the magic is.  AllSeen is the most useful
today because they offer AllJoyn out of the box but nowhere do they
specify the protocol.  For a standard to take off, you should define the
protocol, and offer a reference implementation.  That way other folks
can implement their own version.  Security Now has a &lt;a href="http://twit.tv/show/security-now/464"&gt;little more
info&lt;/a&gt;.&lt;/p&gt;</summary></entry><entry><title>Lake Travis Data #681</title><link href="//fattuba.com/lake-travis-data" rel="alternate"></link><updated>2014-07-18T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-07-18:lake-travis-data</id><summary type="html">&lt;p&gt;Here&amp;#8217;s a &lt;a href="https://morph.io/tubaman/travis-uslakes-info"&gt;little
&lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;
to get the level of Lake Travis.  I&amp;#8217;m scraping the official Lake Travis
website once a day to get the data.  You&amp;#8217;ll have to sign in using your
github account to get an &lt;span class="caps"&gt;API&lt;/span&gt;&amp;nbsp;key.&lt;/p&gt;</summary></entry><entry><title>J.A.R.V.I.S. Milestone</title><link href="//fattuba.com/jarvis-milestone" rel="alternate"></link><updated>2014-07-10T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-07-10:jarvis-milestone</id><summary type="html">&lt;p&gt;I hit a major milestone today in my little Jarvis project.  I got Dragon
Naturally Speaking to execute arbitrary shell commands though an &lt;span class="caps"&gt;SSH&lt;/span&gt;
session.  This is pretty much the last link in the chain of awesomeness.
Here&amp;#8217;s the&amp;nbsp;flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I speak the word &amp;#8220;hello&amp;#8221; into the mic on my earbuds connected to
      my laptop running&amp;nbsp;Debian&lt;/li&gt;
&lt;li&gt;That audio goes into a Windows &lt;span class="caps"&gt;XP&lt;/span&gt; guest running under&amp;nbsp;VirtualBox&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dragon Naturally Speaking running on Windows performs voice
      recognition on it and turns it into&amp;nbsp;text&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;That speech is run through a grammar written in Python configured
      using natlink and&amp;nbsp;Dragonfly&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WelcomeRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompoundRule&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;                  &lt;span class="c"&gt;# Spoken form of command.&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_process_recognition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extras&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;   &lt;span class="c"&gt;# Callback when command is spoken.&lt;/span&gt;
        &lt;span class="n"&gt;spd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;spd-say -p -20 -r -50 &amp;#39;Welcome home, Sir!&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;spd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;enter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;enter&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;enter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;That grammar sends text-to-speech shell&amp;nbsp;command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;spd-say -p -20 -r -50 &amp;#39;Welcome home, Sir!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&amp;#8230;to the open Putty window already &lt;span class="caps"&gt;SSH&lt;/span&gt;&amp;#8217;d back to the Debian&amp;nbsp;host&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The speech dispatcher sends the text to festival which outputs, &amp;#8220;Welcome
      home, Sir!&amp;#8221; though my&amp;nbsp;earbuds.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thanks to &lt;a href="https://www.youtube.com/watch?v=8SkdfdXWYaI"&gt;Tavis Rudd&lt;/a&gt;
for the&amp;nbsp;inspiration.&lt;/p&gt;</summary></entry><entry><title>Errant Whitespace Matters</title><link href="//fattuba.com/errant-whitespace-matters" rel="alternate"></link><updated>2014-07-09T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-07-09:errant-whitespace-matters</id><summary type="html">&lt;blockquote&gt;
&lt;p&gt;Ugh!  Why worry about something trivial like extra whitespace at the
end of a line!!?  Do you have mental problems or something or do you
just hate&amp;nbsp;me?&lt;/p&gt;
&lt;p&gt;&amp;#8212; Software Engineers Just Tryin&amp;#8217; to Get Stuff Done&lt;sup&gt;&lt;span class="caps"&gt;TM&lt;/span&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Confession time.  I think asking developers to remove extra whitespace
is important.  This is a touchy subject but I think I can make a case
so here&amp;nbsp;goes.&lt;/p&gt;
&lt;h3&gt;Reason #1:&amp;nbsp;Quality&lt;/h3&gt;
&lt;p&gt;Is software engineering science or art?  At this point in my life I&amp;#8217;m
leaning in the art direction(I should definitely flesh out that
idea more someday).  Like other forms of art, craftsmanship matters.
Think about a woodworker building a chair.  Yes, the chair needs to not
fall over when you sit in it.  But it also matters how beautiful the
finish is.  The point is that details matter.  If your shiny new code
that implements that sweet new feature includes extra whitespace then
you probably didn&amp;#8217;t even bother to double check your commit, did you?
If you didn&amp;#8217;t bother to double check your commit, there&amp;#8217;s a good chance
that there are other issues &lt;em&gt;besides&lt;/em&gt; the whitespace.  Face it&amp;#8230; you&amp;#8217;re
one of those artsy fartsy types now.  It&amp;#8217;s just that you&amp;#8217;re medium
is bits instead of wood.  Take pride in your&amp;nbsp;work.&lt;/p&gt;
&lt;h3&gt;Reason #2: Slippery&amp;nbsp;Slope&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve seen this a&amp;nbsp;bunch:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;commit b41c6310ede83493c2202bc2e3757882b1351a28
Author: Damnthe Torpedoes &amp;lt;geterdone@agilecompany.com&amp;gt;
Date:   Wed Jul 2 15:36:19 2014 -0500

    [ticket #42] Switch to using  ball bearings

    It&amp;#39;s all ball bearings nowadays
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&amp;#8230;with a diff that contains the ball bearing switch over&amp;#8230;. along
with a bunch of formatting changes automatically introduced by Damnthe&amp;#8217;s
helpful text editor.  Those unnecessary changes makes it almost impossible
to see the actual code change amongst the hundreds of lines of code that
the editor modified.  How am I supposed to review code like that?! How
much of this extra junk should be allowed in a commit before somebody
yells?  I say rather than debate the degree, just say&amp;nbsp;&amp;#8220;none&amp;#8221;.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Listen whiners&amp;#8230; errant whitespace is one of those things that&amp;#8217;s super
easy to check using a&amp;nbsp;script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="go"&gt;git diff-index --check --cached $against --&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So just do it.  It&amp;#8217;ll make you a better developer.  Also it&amp;#8217;ll make us
&lt;span class="caps"&gt;OCD&lt;/span&gt; types twitch just a little bit&amp;nbsp;less.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;P.S.&lt;/span&gt; I found errant whitespace in this post and fixed it before&amp;nbsp;committing.&lt;/p&gt;</summary></entry><entry><title>My Dream Job</title><link href="//fattuba.com/my-dream-job" rel="alternate"></link><updated>2014-07-03T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-07-03:my-dream-job</id><summary type="html">&lt;p&gt;I have some idea of what my &amp;#8220;dream job&amp;#8221; would be like.  I love software so it
would have something to do with that.  I also like working with smart, talented
people so there would have to be some of those.  Oh yeah, also I don&amp;#8217;t
like being micro-managed so there should be none of that.  Then it hits
me&amp;#8230; I think I&amp;#8217;m pretty close to my dream job&amp;nbsp;now!&lt;/p&gt;
&lt;p&gt;I work at a little company called Key Ingredient where we make
a recipe web site.  But calling Key Ingredient a recipe web site
company is like calling Amazon a book store.  We&amp;#8217;re doing a ton of
&lt;a href="http://myfamilyvault.com"&gt;other&lt;/a&gt; &lt;a href="http://keydata.info"&gt;interesting&lt;/a&gt;&amp;nbsp;things.&lt;/p&gt;
&lt;p&gt;So I&amp;#8217;m just going to finish it out and make this my dream job.  Here&amp;#8217;s the&amp;nbsp;plan:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Software&lt;ul&gt;
&lt;li&gt;The company will fund training and conferences for the developers
    including me.  All good devs love to learn about new cool stuff
    so we&amp;#8217;re going to make that&amp;nbsp;happen.&lt;/li&gt;
&lt;li&gt;Meetups and knowledge sharing - devs are encouraged to attend meetups
    and give talks about what they&amp;#8217;re&amp;nbsp;learned.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Smart, talented people.  It&amp;#8217;s pretty simple here&amp;#8230; hire smart people that
      can get things&amp;nbsp;done.&lt;/li&gt;
&lt;li&gt;No micro-management.  I don&amp;#8217;t want to work in a micro-managed environment
      so I refuse to micro-manage my folks.  I&amp;#8217;ve had the great privilege of
      working for some incredible managers who I barely saw because they stayed
      out of the way and let me do my thing.  Here&amp;#8217;s how we&amp;#8217;re rolling:&lt;ul&gt;
&lt;li&gt;Languages, libraries, and tools - each development team picks
    their own.  No mandates&amp;nbsp;here.&lt;/li&gt;
&lt;li&gt;Developers get to pick what they work on.  If you&amp;#8217;re a Javascript expert
    who wants to try their hand at Python, go for it.  If you&amp;#8217;re a backend
    dev who wants to do a little Android, have at&amp;nbsp;it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Anyway, I&amp;#8217;m just 9 months into this journey but it seems to be going well so
far.  I&amp;#8217;ll keep you posted on how my dream job is&amp;nbsp;going.&lt;/p&gt;</summary></entry><entry><title>Easy PDF Templates</title><link href="//fattuba.com/easy-pdf-templates" rel="alternate"></link><updated>2014-07-02T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-07-02:easy-pdf-templates</id><summary type="html">&lt;p&gt;We have a standard contractor agreement we use at
&lt;a href="http://keyingredient.com"&gt;work&lt;/a&gt;.  It&amp;#8217;s pretty cookie cutter but for every
contractor certain things needed to be edited.  These edits included
name of the contractor, address, start date of the contract, etc.
After giving up on forms in LibreOffice, I found an elegant solution.
It turns out that LibreOffice od* files are just zipped &lt;span class="caps"&gt;XML&lt;/span&gt;.  So I
created an Open Document Template(odt) file and replaced all the relevant
data with placeholders like &lt;code&gt;__CONTRACTOR__&lt;/code&gt; for contractor name.  Then I
use a simple make file&amp;nbsp;to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;unzip the&amp;nbsp;template&lt;/li&gt;
&lt;li&gt;fill in the placeholders using&amp;nbsp;sed&lt;/li&gt;
&lt;li&gt;zip it back&amp;nbsp;up&lt;/li&gt;
&lt;li&gt;convert it to a &lt;span class="caps"&gt;PDF&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&amp;#8217;s the&amp;nbsp;Makefile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;Independent-Contractor-Agmt.pdf&lt;/span&gt;

&lt;span class="nv"&gt;CONTRACTOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Foobar Industries
&lt;span class="nv"&gt;CONTRACTORLOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Austin, TX
&lt;span class="nv"&gt;CONTRACTPERIOD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 months
&lt;span class="nv"&gt;BEGINDATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;shell date +&lt;span class="s2"&gt;&amp;quot;%B %d, %Y&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ENDDATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;shell date -d &lt;span class="s2"&gt;&amp;quot;+$(CONTRACTPERIOD)&amp;quot;&lt;/span&gt; +&lt;span class="s2"&gt;&amp;quot;%B %d, %Y&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;%.odt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;%.ott&lt;/span&gt;
  mkdir &lt;span class="nv"&gt;$*&lt;/span&gt;
  &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$*&lt;/span&gt;; unzip ../&lt;span class="nv"&gt;$&amp;lt;&lt;/span&gt;
  sed -i &lt;span class="s1"&gt;&amp;#39;s/__CONTRACTOR__/$(CONTRACTOR)/g&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;$*&lt;/span&gt;/content.xml
  sed -i &lt;span class="s1"&gt;&amp;#39;s/__CONTRACTORLOCATION__/$(CONTRACTORLOCATION)/g&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;$*&lt;/span&gt;/content.xml
  sed -i &lt;span class="s1"&gt;&amp;#39;s/__BEGINDATE__/$(BEGINDATE)/g&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;$*&lt;/span&gt;/content.xml
  sed -i &lt;span class="s1"&gt;&amp;#39;s/__ENDDATE__/$(ENDDATE)/g&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;$*&lt;/span&gt;/content.xml
  sed -i &lt;span class="s1"&gt;&amp;#39;s/__CONTRACTPERIOD__/$(CONTRACTPERIOD)/g&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;$*&lt;/span&gt;/content.xml
  &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$*&lt;/span&gt;; zip -r ../&lt;span class="nv"&gt;$@&lt;/span&gt; *
  rm -r &lt;span class="nv"&gt;$*&lt;/span&gt;

&lt;span class="nf"&gt;%.pdf&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;%.odt&lt;/span&gt;
  unoconv -f pdf &lt;span class="nv"&gt;$&amp;lt;&lt;/span&gt;

&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  rm -f *.odt
  rm -f *.pdf
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And here&amp;#8217;s how I run it from the command&amp;nbsp;line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;CONTRACTOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Contracts R Us&amp;quot;&lt;/span&gt; \
&lt;span class="n"&gt;CONTRACTORLOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1234 Fantasy Ln, Austin, TX&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;There are defaults for all the placeholders in the Makefile and you can override
them on the command line.  For example, &lt;code&gt;BEGINDATE&lt;/code&gt; is the current date
by default.  I originally tried using m4 instead of sed but it wasn&amp;#8217;t
able to parse the &lt;span class="caps"&gt;XML&lt;/span&gt; file.  This is just another example of doing things
the &lt;span class="caps"&gt;UNIX&lt;/span&gt; Way&lt;sup&gt;&lt;span class="caps"&gt;TM&lt;/span&gt;&lt;/sup&gt;.&lt;/p&gt;</summary></entry><entry><title>Small Accounting Software Sucks</title><link href="//fattuba.com/small-accounting-software-sucks" rel="alternate"></link><updated>2014-06-30T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-06-30:small-accounting-software-sucks</id><summary type="html">&lt;p&gt;All home or small business accounting software sucks.  I&amp;#8217;ve tried Quicken
for home, and Quickbooks for business.  I stuck with ledgersmb for a long
time with my business but the web interface was pretty
old and clunky.  Right now I&amp;#8217;m playing around with
ledger-cli.  Have something that you&amp;#8217;re happy with?  &lt;a href="mailto:tubaman@fattuba.com?Subject=I%20loves%20me%20some%20accounting%20software!"&gt;Let me
know&lt;/a&gt;.&lt;/p&gt;</summary></entry><entry><title>Snow Day</title><link href="//fattuba.com/snowday" rel="alternate"></link><updated>2014-06-29T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-06-29:snowday</id><summary type="html">&lt;p&gt;Sylvia Nowakowski Day&amp;#8230; Sylvia Now Day&amp;#8230; S. Now Day&amp;#8230; Snow&amp;nbsp;Day!&amp;#8230;&lt;/p&gt;
&lt;p&gt;&lt;img src="//fattuba.com/images/snowday.jpg" alt="Sylvia's Day"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;p&gt;In a crazy, unbelievable, beautiful twist, we are parents.  God took us from 0
to 100 in a little over 2 years.  Here&amp;#8217;s the brief&amp;nbsp;history:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;January 2012 - Just me and my bride.  No desire for&amp;nbsp;kids.&lt;/li&gt;
&lt;li&gt;March 2012 - Several long talks and prayer.  It turns out my wife and I
     both want to adopt.  After considering private, international and foster
     care adoption, we decide that foster care was it for us.  If you want the
     details, &lt;a href="mailto:tubaman@fattuba.com"&gt;email me&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;May 2012 - We reach out to &lt;a href="http://pathway.org/"&gt;Pathways&lt;/a&gt;, an
     adoption and foster care services&amp;nbsp;non-profit.&lt;/li&gt;
&lt;li&gt;July 2012 - We begin our foster care training classes.  What have we gotten
     ourselves&amp;nbsp;into?&lt;/li&gt;
&lt;li&gt;August 2012 - What ages do we want to get licensed to foster?  Let&amp;#8217;s do
     ages 5 to&amp;nbsp;11.&lt;/li&gt;
&lt;li&gt;September 2012 - Reconsidering the age range&amp;#8230; not sure&amp;#8230; why not ages 0
     to&amp;nbsp;17?&lt;/li&gt;
&lt;li&gt;October 2012 - We schedule our home&amp;nbsp;study.&lt;/li&gt;
&lt;li&gt;March 2013 - We hear about a 16 year old girl from Laredo who
     wants to be&amp;nbsp;adopted.&lt;/li&gt;
&lt;li&gt;April 2013 - We tell our adoption coordinator that we&amp;#8217;re interested
     in learning more more about&amp;nbsp;her.&lt;/li&gt;
&lt;li&gt;June 2013 - We talk to Sylvia for the first time over video chat.  We head
     down to Laredo to meet her in&amp;nbsp;person.&lt;/li&gt;
&lt;li&gt;July 2013 - Sylvia decides that we&amp;#8217;re her new parents!  The state
     places her with us as her foster parents.  We take her down to South
     Padre to my family&amp;nbsp;reunion.&lt;/li&gt;
&lt;li&gt;June 2014 - The adoption is finally&amp;nbsp;consummated!&lt;/li&gt;
&lt;/ul&gt;</summary></entry><entry><title>How Is Pelican?</title><link href="//fattuba.com/how-is-pelican" rel="alternate"></link><updated>2014-06-28T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2014-06-28:how-is-pelican</id><summary type="html">&lt;p&gt;I&amp;#8217;m sort of jazzed about static site generators right now for some reason.
And since I needed to build a developer blog at Key Ingredient, it seemed
like a good opportunity.  After checking out
&lt;a href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;, &lt;a href="http://jaspervdj.be/hakyll/"&gt;Hakyll&lt;/a&gt;,
&lt;a href="http://hyde.github.io/"&gt;Hyde&lt;/a&gt; &lt;a href="http://getnikola.com/"&gt;Nikola&lt;/a&gt; and
&lt;a href="http://docs.getpelican.com/"&gt;Pelican&lt;/a&gt; I ended up picking&amp;nbsp;Pelican.&lt;/p&gt;
&lt;p&gt;I considered Jekyll because it&amp;#8217;s got a lot of buzz around it right now which
&lt;em&gt;does&lt;/em&gt; count for something.  Many times buzz equals users which equals features
added more quickly.  However, I really wanted something in Python in case I ever
needed to hack on it(Ruby is not my favorite).  Hakyll was also out for the same&amp;nbsp;reason.&lt;/p&gt;
&lt;p&gt;So that leaves Hyde, Nikola and Pelican.  The Hyde project seems poorly
managed with the &lt;a href="http://ringce.com/hyde"&gt;old site&lt;/a&gt; lying around.
Also there hasn&amp;#8217;t been much progress on the project in a while.
Nikola seems great!  The documentation is good and it seems mature.
For whatever reason it just didn&amp;#8217;t click with&amp;nbsp;me.&lt;/p&gt;
&lt;p&gt;That leaves Pelican.  Pelican just makes sense to me.  The quickstart
script, multiple build options, and config setting seem like something
I would write for myself if I were so inclined.  I really like the fact
that I can write in markdown, restructured text, or asciidoc even though
I&amp;#8217;ll stick mostly with markdown since I&amp;#8217;m used to it(github and Unfuddle also
use markdown).  I also like the fact that Pelican uses Jinja for theme
templates because we use it for the templates on&amp;nbsp;keyingredient.com.&lt;/p&gt;
&lt;p&gt;For the dev blog, I started with the Bootstrap 2 Pelican theme, upgraded
it to Bootstrap 3 and added our logo.  Here&amp;#8217;s the result:
&lt;img
src="https://github.com/KeyIngredient/pelican-theme-ki-bootstrap/raw/master/screenshot.png" alt="Pelican Theme Screenshot" /&gt;
&amp;#8230;and the &lt;a href="https://github.com/KeyIngredient/pelican-theme-ki-bootstrap"&gt;code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once the Key Ingredient developer blog was up and running I turned my attention
to this site.  Fattuba had been languishing for years and my boss Harlan
gave me the push I needed to overhaul the thing&amp;nbsp;already!&lt;/p&gt;</summary></entry><entry><title>Open Letter to Embedded Hardware Manufacturers</title><link href="//fattuba.com/embedded-hardware-letter" rel="alternate"></link><updated>2012-06-13T00:00:00-05:00</updated><author><name>Ryan Nowakowski</name></author><id>tag://fattuba.com,2012-06-13:embedded-hardware-letter</id><summary type="html">&lt;p&gt;Dear Embedded Hardware&amp;nbsp;Manufacturers,&lt;/p&gt;
&lt;p&gt;We are embedded Linux developers.  This is 2012.  What&amp;nbsp;gives?&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s time embedded hardware manufacturers get with the times.  You may
be able to fool the suits but you haven&amp;#8217;t convinced us. That is, you
haven&amp;#8217;t convinced us that your semi-walled garden is the way to go.
Yes, you offer Linux support in your &lt;span class="caps"&gt;BSP&lt;/span&gt;.  But that is no longer enough.
You see, we have gotten use to Googling for answers, asking for help on
&lt;span class="caps"&gt;IRC&lt;/span&gt; and seeking advice from peers via email.  Dial-in support numbers
are useless and &amp;#8220;live&amp;#8221; chats from your website make us feel&amp;nbsp;ill.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re tired of &lt;a href="http://ltib.org/"&gt;hacky build systems&lt;/a&gt; that only support
&lt;em&gt;your&lt;/em&gt; platforms.  What does it say about your &amp;#8220;Linux support&amp;#8221; if your
patches aren&amp;#8217;t good enough for&amp;nbsp;mainline?&lt;/p&gt;
&lt;p&gt;We build our own kernels straight from kernel.org.  Support for your
platform should be there.  We run &lt;a href="http://openwrt.org"&gt;OpenWRT&lt;/a&gt; on our
own routers at home.  Your platform should be there when I type &lt;code&gt;make
menuconfig&lt;/code&gt;.  If you have &amp;#8220;secret sauce&amp;#8221; that makes your platform better
than the competitors, it better be easy to build, integrate, and&amp;nbsp;use.&lt;/p&gt;
&lt;p&gt;In short if you want us to recommend you and your platform to the&amp;nbsp;suits&amp;#8230;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have a great&amp;nbsp;hardware&lt;/li&gt;
&lt;li&gt;Get your platform into Linux mainline - Sometimes this can make up for slightly weaker&amp;nbsp;hardware&lt;/li&gt;
&lt;li&gt;Get your platform into OpenWRT - Sometimes this can make up for non-mainline Linux&amp;nbsp;support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sincerely,&lt;/p&gt;
&lt;p&gt;Embedded Linux&amp;nbsp;Developers&lt;/p&gt;</summary></entry></feed>