<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Samat Says</title><link>https://blog.samat.org/</link><description>Samat K Jain's personal blog</description><atom:link href="https://blog.samat.org/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Fri, 29 Jun 2018 09:25:27 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Deprecating functions and methods in C++</title><link>https://blog.samat.org/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;Refactoring a C++ code base for a binary API, I needed to deprecate some functions and communicate changes to API users.
While noted in the API's documentation, people don't read documentation, so I wanted the compiler to warn users as well (not that people read compiler warnings either!).&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3760.html"&gt;C++14 has a [[deprecated]] attribute&lt;/a&gt;, but what if you want to use that if supported, and do something else for other compilers?&lt;/p&gt;
&lt;p&gt;Macros to the rescue! Below will use the canonical C++14 way if supported, and an equivalent proprietary method for GCC and Microsoft Visual C++ respectively.&lt;/p&gt;
&lt;pre class="code text"&gt;&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-1"&gt;&lt;/a&gt;// Helper to deprecate functions and methods
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-2"&gt;&lt;/a&gt;// See https://blog.samat.io/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-3"&gt;&lt;/a&gt;// For C++14
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-4"&gt;&lt;/a&gt;#if __cplusplus &amp;gt;= 201402L
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-5"&gt;&lt;/a&gt;    #if defined(__has_cpp_attribute)
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-6"&gt;&lt;/a&gt;        #if __has_cpp_attribute(deprecated)
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-7"&gt;&lt;/a&gt;            #define DEPRECATED(msg, func) [[deprecated(msg)]] func
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-8"&gt;&lt;/a&gt;        #endif
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-9"&gt;&lt;/a&gt;    #endif
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-10"&gt;&lt;/a&gt;// For everyone else
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-11"&gt;&lt;/a&gt;#else
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-12"&gt;&lt;/a&gt;    #ifdef __GNUC__
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-13"&gt;&lt;/a&gt;        #define DEPRECATED(msg, func) func __attribute__ ((deprecated(msg)))
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-14"&gt;&lt;/a&gt;    #elif defined(_MSC_VER)
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-15"&gt;&lt;/a&gt;        #define DEPRECATED(msg, func) __declspec(deprecated(msg)) func
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-16"&gt;&lt;/a&gt;    #endif
&lt;a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-17"&gt;&lt;/a&gt;#endif
&lt;/pre&gt;&lt;p&gt;To use it: in your header file for you API, simply wrap a function or method declaration with the macro. From:&lt;/p&gt;
&lt;pre class="code c++"&gt;&lt;a name="rest_code_30c4e1761b2e4bf6a545dcf719082d8e-1"&gt;&lt;/a&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;goRadius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;three&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;wrap it like so:&lt;/p&gt;
&lt;pre class="code c++"&gt;&lt;a name="rest_code_0cf2409ced7a473aa34c78fc5a2cc65e-1"&gt;&lt;/a&gt;&lt;span class="n"&gt;DEPRECATED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Use goNew()"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;goRadius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;three&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;And you'll get a warning. Here's what it looks like with GCC 6.2:&lt;/p&gt;
&lt;pre class="code text"&gt;&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-1"&gt;&lt;/a&gt;/api.cpp: In member function ‘void SomeClass::go()’:
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-2"&gt;&lt;/a&gt;/api.cpp:104:23: warning: ‘void SomeClass::go(size_t, float, float, float)’ is deprecated: Use goNew() [-Wdeprecated-declarations]
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-3"&gt;&lt;/a&gt;     go(10, 1, 1, 1);
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-4"&gt;&lt;/a&gt;                   ^
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-5"&gt;&lt;/a&gt;In file included from /api.cpp:17:0:
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-6"&gt;&lt;/a&gt;/api.h:135:37: note: declared here
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-7"&gt;&lt;/a&gt;     DEPRECATED("Use goNew()", void go(size_t goRadius, float one, float two, float three));
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-8"&gt;&lt;/a&gt;                                    ^
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-9"&gt;&lt;/a&gt;/api.h:41:63: note: in definition of macro ‘DEPRECATED’
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-10"&gt;&lt;/a&gt;             #define DEPRECATED(msg, func) [[deprecated(msg)]] func
&lt;a name="rest_code_723ede9bf13843dfadffe6b2a928284c-11"&gt;&lt;/a&gt;                                                           ^~~~
&lt;/pre&gt;&lt;p&gt;The macro is not perfect, however (or rather, compilers are not).&lt;/p&gt;
&lt;p&gt;The canonical way for checking if &lt;tt class="docutils literal"&gt;[[deprecated]]&lt;/tt&gt; is supported is with the compiler definition &lt;tt class="docutils literal"&gt;__has_cpp_attribute(deprecated)&lt;/tt&gt;;
unfortunately, GCC 6.2 defines this symbol regardless of whether you are in C++14 mode or not.
And then it prints a warning when run in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-pedantic&lt;/span&gt;&lt;/tt&gt; mode, even though it's supported!&lt;/p&gt;
&lt;p&gt;In the above snippet, the C++14 method is only used if the compiler fully supports C++14 and is in C++14 mode.
If that's not important to you, consider removing that extra if statement, and use this instead:&lt;/p&gt;
&lt;pre class="code text"&gt;&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-1"&gt;&lt;/a&gt;// Helper to deprecate functions and methods
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-2"&gt;&lt;/a&gt;// See https://blog.samat.io/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-3"&gt;&lt;/a&gt;// For C++14
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-4"&gt;&lt;/a&gt;#if defined(__has_cpp_attribute)
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-5"&gt;&lt;/a&gt;    #if __has_cpp_attribute(deprecated)
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-6"&gt;&lt;/a&gt;        #define DEPRECATED(msg, func) [[deprecated(msg)]] func
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-7"&gt;&lt;/a&gt;    #endif
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-8"&gt;&lt;/a&gt;// For everyone else
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-9"&gt;&lt;/a&gt;#else
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-10"&gt;&lt;/a&gt;    #ifdef __GNUC__
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-11"&gt;&lt;/a&gt;        #define DEPRECATED(msg, func) func __attribute__ ((deprecated(msg)))
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-12"&gt;&lt;/a&gt;    #elif defined(_MSC_VER)
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-13"&gt;&lt;/a&gt;        #define DEPRECATED(msg, func) __declspec(deprecated(msg)) func
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-14"&gt;&lt;/a&gt;    #endif
&lt;a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-15"&gt;&lt;/a&gt;#endif
&lt;/pre&gt;&lt;/div&gt;</description><category>C++</category><category>C++14</category><category>Programming</category><guid>https://blog.samat.org/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/</guid><pubDate>Mon, 27 Feb 2017 00:00:00 GMT</pubDate></item><item><title>Going HTTPS</title><link>https://blog.samat.org/2015/12/02/Going-HTTPS/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;This &lt;a class="reference external" href="https://blog.samat.org/"&gt;blog&lt;/a&gt;, &lt;a class="reference external" href="https://samat.org/"&gt;my landing page&lt;/a&gt;, &lt;a class="reference external" href="https://wiki.samat.org/"&gt;wiki&lt;/a&gt;, and a few of my other Websites are now being served encrypted over HTTPS (see the lock icon in your address bar?), thanks to &lt;a class="reference external" href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;.
Along with TLS, I've enabled HTTP/2.&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://blog.samat.org/images/2015/12/02/blog.samat.org-SSL-screenshot.png"&gt;&lt;img alt="/images/2015/12/02/blog.samat.org-SSL-screenshot.thumbnail.png" class="align-right" src="https://blog.samat.org/images/2015/12/02/blog.samat.org-SSL-screenshot.thumbnail.png"&gt;&lt;/a&gt;
&lt;p&gt;Hackers won't find anything sensitive on my public Websites, but my private Websites (e.g. my ownCloud and Tiny Tiny RSS instances) have needed more security for a long time.&lt;/p&gt;
&lt;p&gt;Enabling HTTP/2 was very easy, as HTTP/2 support is shipped with the 'http2' module in Apache 2.4.17 and later.
While easy, it wasn't obvious; I've written a &lt;a class="reference external" href="https://blog.samat.org/2015/11/26/Enabling-HTTP2-on-Apache-2.4-on-Debian-Ubuntu/"&gt;tutorial for enabling HTTP/2 on Apache&lt;/a&gt;.
Redirecting non-HTTP connections was trickier than I thought, so I've written a tutorial for &lt;a class="reference external" href="https://blog.samat.org/2015/12/02/Going-HTTPS/"&gt;HTTPS redirects with Apache's mod_rewrite&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;I created my TLS certificates as part of &lt;a class="reference external" href="https://community.letsencrypt.org/t/beta-program-announcements/1631"&gt;Let's Encrypt's closed beta&lt;/a&gt;.
I have an unconventional and complex Apache setup (something I'll simplify, one day…)
and because of &lt;a class="reference external" href="https://github.com/letsencrypt/letsencrypt/issues/1531"&gt;bug 1531&lt;/a&gt;, a problem in an upstream library, I can't use the official client the way it was meant to be used (i.e. "install" or "auth" commands).
I don't think I wanted an automated script editing config files on my servers anyway.&lt;/p&gt;
&lt;p&gt;With a lot of fiddling, I've figured out &lt;a class="reference external" href="https://blog.samat.org/2015/12/02/Going-HTTPS/"&gt;how to use the official letsencrypt client reverse proxied through Apache&lt;/a&gt;,
which will let me update certificates regularly without headache.&lt;/p&gt;&lt;/div&gt;</description><category>Apache</category><category>Crypto</category><category>LetsEncrypt.org</category><category>Meta</category><guid>https://blog.samat.org/2015/12/02/Going-HTTPS/</guid><pubDate>Wed, 02 Dec 2015 00:00:00 GMT</pubDate></item><item><title>Enabling HTTP/2 on Apache 2.4 on Debian or Ubuntu</title><link>https://blog.samat.org/2015/11/26/Enabling-HTTP2-on-Apache-2.4-on-Debian-Ubuntu/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;a class="reference external" href="https://httpd.apache.org/docs/2.4/mod/mod_http2.html"&gt;Apache 2.4.17 ships with mod_http2&lt;/a&gt;.
Available in Debian 9 (stretch) and &lt;span class="strike"&gt;Ubuntu 16.04 (Xenial Xerus)&lt;/span&gt; (see comments) Ubuntu 16.10 (Yakkety Yak),
it brings HTTP/2 support to one of the Internet's popular Web servers.
Assuming you've already configured a SSL/TLS Website, this quick tutorial will show you how to quickly enable HTTP/2.&lt;/p&gt;
&lt;p&gt;Based on &lt;a class="reference external" href="https://icing.github.io/mod_h2/"&gt;mod_h2&lt;/a&gt;, the module is still very experimental.
It should be enabled manually, on a site-by-site basis, via the &lt;a class="reference external" href="https://httpd.apache.org/docs/2.4/mod/core.html#protocols"&gt;Protocols directive&lt;/a&gt;.
The module's defaults otherwise don't need to be changed.&lt;/p&gt;
&lt;p&gt;First, enable the module:&lt;/p&gt;
&lt;pre class="code sh"&gt;&lt;a name="rest_code_40dd213b29044b90a0274126674c0091-1"&gt;&lt;/a&gt;sudo a2enmod http2
&lt;/pre&gt;&lt;p&gt;In the &amp;lt;VirtualHost&amp;gt; stanzas for your Website served over TLS in your Apache configuration, add the Protocols directive:&lt;/p&gt;
&lt;pre class="code apache"&gt;&lt;a name="rest_code_144f128ccf994f22abc932a08616bfab-1"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;VirtualHost&lt;/span&gt; &lt;span class="s"&gt;*:443&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_144f128ccf994f22abc932a08616bfab-2"&gt;&lt;/a&gt;  &lt;span class="nb"&gt;ServerName&lt;/span&gt; example.com
&lt;a name="rest_code_144f128ccf994f22abc932a08616bfab-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_144f128ccf994f22abc932a08616bfab-4"&gt;&lt;/a&gt;  &lt;span class="nb"&gt;Protocols&lt;/span&gt; h2 http/1.1
&lt;a name="rest_code_144f128ccf994f22abc932a08616bfab-5"&gt;&lt;/a&gt;
&lt;a name="rest_code_144f128ccf994f22abc932a08616bfab-6"&gt;&lt;/a&gt;  &lt;span class="c"&gt;# Other configuration stuff here…&lt;/span&gt;
&lt;a name="rest_code_144f128ccf994f22abc932a08616bfab-7"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Restart Apache:&lt;/p&gt;
&lt;pre class="code sh"&gt;&lt;a name="rest_code_c1307c7b66dc472d9ff614cfac9f5def-1"&gt;&lt;/a&gt;sudo service apache2 restart
&lt;/pre&gt;&lt;p&gt;If you've curl 7.34.0 or later, you can test whether HTTP/2 is working by running:&lt;/p&gt;
&lt;pre class="code sh"&gt;&lt;a name="rest_code_eceda1c831cd4a80b484e124d3e8850b-1"&gt;&lt;/a&gt;curl -k -v --http2 https://example.com/
&lt;/pre&gt;&lt;p&gt;and look for mentions of "http2".&lt;/p&gt;
&lt;p&gt;While you're fiddling with your Web server configuration, consider updating your SSL settings with &lt;a class="reference external" href="https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=apache-2.4.17"&gt;Mozilla's great SSL configuration generator&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;</description><category>Apache</category><category>Crypto</category><category>Debian</category><category>Ubuntu</category><guid>https://blog.samat.org/2015/11/26/Enabling-HTTP2-on-Apache-2.4-on-Debian-Ubuntu/</guid><pubDate>Thu, 26 Nov 2015 00:00:00 GMT</pubDate></item><item><title>Getting All Past Events for your Meetup.com group</title><link>https://blog.samat.org/2015/10/23/Getting-All-Past-Meetup-Events/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;If you're an organizer on Meetup.com, it's a pain to look for events that have happened in the past in your group.
Meetup doesn't have a way to search only within your group, and paging through the calendar is a pain if your group is old or if you have a lot of events.&lt;/p&gt;
&lt;p&gt;I've written a quick Python script that uses &lt;a class="reference external" href="http://www.meetup.com/meetup_api/"&gt;Meetup.com's API&lt;/a&gt; to get all of a Meetup group's meetup events and generate an easy-to-search and view report.
For the &lt;a class="reference external" href="http://jornadahikers.com/"&gt;Jornada Hiking &amp;amp; Outdoors Club&lt;/a&gt;, you can &lt;a class="reference external" href="http://stuff.jornadahikers.com/all-past-events.html"&gt;see a report of all our previous events&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://gist.github.com/samatjain/5ae8860b0754eb1fa3cc"&gt;script is on Github as a gist&lt;/a&gt; if you'd to run it against your own Meetup.com groups.
You'll need to know the group's "custom address" (which is also in the URL for your group) and have an &lt;a class="reference external" href="https://secure.meetup.com/meetup_api/key/"&gt;Meetup.com API key&lt;/a&gt;.&lt;/p&gt;
&lt;script src="https://gist.github.com/5ae8860b0754eb1fa3cc.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre class="literal-block"&gt;
#!/usr/bin/env python3

import collections
import datetime
import pprint

import click
import jinja2
import requests

template = '''
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&amp;gt;
        &amp;lt;meta name=viewport content="width=device-width, initial-scale=1.0"&amp;gt;
        &amp;lt;style type="text/css"&amp;gt;
            td.date_pretty {
                text-align: right;
            }
        &amp;lt;/style&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Previous events for &amp;lt;a href="http://www.meetup.com/{{ group_name }}/"&amp;gt;{{ group_name }}&amp;lt;/a&amp;gt;
        {% for grouping_name, events in groupings.items() %}
        &amp;lt;h2&amp;gt;{{ grouping_name }}&amp;lt;/h2&amp;gt;
        &amp;lt;table&amp;gt;{% for i in events %}
            &amp;lt;tr&amp;gt;&amp;lt;td class="date_pretty"&amp;gt;{{ i.date_pretty }}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;a href="{{ i.event_url }}"&amp;gt;{{ i.name }}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;
         {%- endfor %}&amp;lt;/table&amp;gt;
        {% endfor %}
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
'''

default_payload = { 'status': 'past' }

def generate_html(group_name, g):
    global template

    je = jinja2.Environment()
    jt = je.from_string(template)

    out = jt.render(group_name=group_name, groupings=g)
    return out


def generate_events(group_name, api_key):
    offset = 0
    while True:
        offset_payload = { 'offset': offset,
                           'key': api_key,
                           'group_urlname': group_name }
        payload = default_payload.copy()
        payload.update(offset_payload)
        # Above is the equivalent of jQuery.extend()
        # for Python 3.5: payload = {**default_payload, **offset_payload}

        r = requests.get('https://api.meetup.com/2/events', params=payload)
        json = r.json()

        results, meta = json['results'], json['meta']
        for item in results:
            yield item

        # if we no longer have more results pages, stop…
        if not meta['next']:
            return

        offset = offset + 1


@click.command()
@click.option('--groupname', 'group_name', default='jornadahikers', help='Name of group in Meetup.com URL, i.e. http://meetup.com/&amp;lt;groupname&amp;gt;/')
@click.option('--apikey', 'api_key', envvar='MEETUP_API_KEY', help='Your Meetup.com API key, from https://secure.meetup.com/meetup_api/key/')
def go(group_name, api_key):

    all_events = list(generate_events(group_name, api_key))
    for event in all_events:
        # convert time returned by Meetup API
        time = int(event['time'])/1000
        time_obj = datetime.datetime.fromtimestamp(time)

        # create a pretty-looking date, and group by month
        date_pretty = time_obj.strftime('%a %b %-d')
        grouping_name = time_obj.strftime('%b %Y')

        event['grouping_name'] = grouping_name
        event['date_pretty'] = date_pretty

    # group by month
    groupings = collections.OrderedDict()
    for event in all_events:
        grouping_name = event['grouping_name']

        grouping = groupings.get(grouping_name, [])
        grouping.append(event)
        groupings[grouping_name] = grouping

    print(generate_html(group_name, groupings))

if __name__ == '__main__':
    go()

&lt;/pre&gt;
&lt;/noscript&gt;&lt;p&gt;After installing the dependencies (Python 3.4+, click, jinja2, and requests), usage is pretty easy:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Usage: Meetup-past-events.py [OPTIONS]&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Options:&lt;/dt&gt;
&lt;dd&gt;&lt;table class="first last docutils option-list" frame="void" rules="none"&gt;
&lt;col class="option"&gt;
&lt;col class="description"&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="option-group" colspan="2"&gt;
&lt;kbd&gt;&lt;span class="option"&gt;--groupname &lt;var&gt;TEXT&lt;/var&gt;&lt;/span&gt;&lt;/kbd&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;td&gt;Name of group in Meetup.com URL, i.e.
&lt;a class="reference external" href="http://meetup.com"&gt;http://meetup.com&lt;/a&gt;/&amp;lt;groupname&amp;gt;/&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="option-group"&gt;
&lt;kbd&gt;&lt;span class="option"&gt;--apikey &lt;var&gt;TEXT&lt;/var&gt;&lt;/span&gt;&lt;/kbd&gt;&lt;/td&gt;
&lt;td&gt;Your Meetup.com API key, from
&lt;a class="reference external" href="https://secure.meetup.com/meetup_api/key/"&gt;https://secure.meetup.com/meetup_api/key/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="option-group"&gt;
&lt;kbd&gt;&lt;span class="option"&gt;--help&lt;/span&gt;&lt;/kbd&gt;&lt;/td&gt;
&lt;td&gt;Show this message and exit.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you've found this blog post, downloading and running the script might be too much for you.
Feel free to &lt;a class="reference external" href="https://samat.org/contact/"&gt;contact me&lt;/a&gt; with a link to your group and I'll e-mail a list for your group.&lt;/p&gt;&lt;/div&gt;</description><category>Hack</category><category>Meetup</category><category>Python</category><guid>https://blog.samat.org/2015/10/23/Getting-All-Past-Meetup-Events/</guid><pubDate>Fri, 23 Oct 2015 00:00:00 GMT</pubDate></item><item><title>Slimming an existing Raspbian install</title><link>https://blog.samat.org/2015/02/05/slimming-an-existing-raspbian-install/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;a class="reference external" href="http://www.raspbian.org/"&gt;Raspbian&lt;/a&gt; is the definitive full-featured Linux distribution for the
&lt;a class="reference external" href="http://www.raspberrypi.org/"&gt;Raspberry Pi&lt;/a&gt;.
As it is tailored for educational use, there is a lot of software that is unnecessary (i.e. bloat) if you intend to use your Pi headless.&lt;/p&gt;
&lt;p&gt;While various slimmed down Linux distributions exist for the Raspberry Pi, what if you want to slim down an existing Raspbian install?&lt;/p&gt;
&lt;p&gt;I encountered this problem when I hosted a Raspberry Pi with &lt;a class="reference external" href="http://raspberrycolocation.com/"&gt;Raspberry Pi
Colocation&lt;/a&gt; from &lt;a class="reference external" href="https://www.pcextreme.nl/en"&gt;PCextreme&lt;/a&gt;. You'll get a Raspberry Pi with stock Raspbian,
complete with X11 and a bunch of other  software unnecessary for use in a
datacenter.&lt;/p&gt;
&lt;p&gt;Based on the &lt;a class="reference external" href="https://github.com/RPi-Distro/spindle"&gt;official Raspbian build scripts&lt;/a&gt; (previously &lt;a class="reference external" href="https://github.com/asb/spindle"&gt;asb/spindle&lt;/a&gt;), you can copy and paste the commands below to uninstall all the extra educational and GUI packages:&lt;/p&gt;
&lt;script src="https://gist.github.com/4dda24e14a5b73481e2a.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre class="literal-block"&gt;
#!/bin/bash

# GUI-related packages
pkgs="
xserver-xorg-video-fbdev
xserver-xorg xinit
gstreamer1.0-x gstreamer1.0-omx gstreamer1.0-plugins-base
gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-alsa
gstreamer1.0-libav
epiphany-browser
lxde lxtask menu-xdg gksu
xserver-xorg-video-fbturbo
xpdf gtk2-engines alsa-utils
netsurf-gtk zenity
desktop-base lxpolkit
weston
omxplayer
raspberrypi-artwork
lightdm gnome-themes-standard-data gnome-icon-theme
qt50-snapshot qt50-quick-particle-examples
"

# Edu-related packages
pkgs="$pkgs
idle python3-pygame python-pygame python-tk
idle3 python3-tk
python3-rpi.gpio
python-serial python3-serial
python-picamera python3-picamera
python3-pygame python-pygame python-tk
python3-tk
debian-reference-en dillo x2x
scratch nuscratch
timidity
smartsim penguinspuzzle
pistore
sonic-pi
python3-numpy
python3-pifacecommon python3-pifacedigitalio python3-pifacedigital-scratch-handler python-pifacecommon python-pifacedigitalio
oracle-java8-jdk
minecraft-pi python-minecraftpi
wolfram-engine
"
# Because of of https://github.com/RPi-Distro/raspberrypi-ui-mods/issues/2 (thanks @robertely)
echo apt-get -y remove raspberrypi-ui-mods

# Remove packages
for i in $pkgs; do
	echo apt-get -y remove --purge $i
done

# Remove automatically installed dependency packages
echo apt-get -y autoremove

# Remove all packages marked rc (thanks @symm)
dpkg --list |grep "^rc" | cut -d " " -f 3 | xargs dpkg --purge 
&lt;/pre&gt;
&lt;/noscript&gt;&lt;p&gt;The above snippet is &lt;a class="reference external" href="https://gist.github.com/samatjain/4dda24e14a5b73481e2a"&gt;available in Github as a gist&lt;/a&gt;—I'll keep it up to date, and feel free to fork it and add/remove whatever other packages you find necessary.&lt;/p&gt;
&lt;p&gt;Download and run it with:&lt;/p&gt;
&lt;pre class="code sh"&gt;&lt;a name="rest_code_77f873d4db4347b28c2294d51be0cb7e-1"&gt;&lt;/a&gt;sudo cp /etc/network/interfaces /etc/network/interfaces.bak
&lt;a name="rest_code_77f873d4db4347b28c2294d51be0cb7e-2"&gt;&lt;/a&gt;wget https://gist.githubusercontent.com/samatjain/4dda24e14a5b73481e2a/raw/5d9bac8ec40b94833b4e9938121945be252fdee1/Slim-Raspbian.sh -O Slim-Raspbian.sh
&lt;a name="rest_code_77f873d4db4347b28c2294d51be0cb7e-3"&gt;&lt;/a&gt;sh ./Slim-Raspbian.sh &lt;span class="p"&gt;|&lt;/span&gt; sudo sh
&lt;/pre&gt;&lt;p&gt;The above backups up &lt;tt class="docutils literal"&gt;/etc/network/interfaces&lt;/tt&gt; in case it's deleted (see comments).&lt;/p&gt;
&lt;p&gt;Coming soon: how to create your own Raspbian image!&lt;/p&gt;
&lt;p&gt;UPDATE [19 Aug 2015]: &lt;a class="reference external" href="https://github.com/robertely"&gt;Robert Ely&lt;/a&gt; pointed out that purging raspberrypi-ui-mods will delete your network config—I've updated the gist so this package is only removed, not purged. I've also added a line from &lt;a class="reference external" href="https://gist.github.com/symm"&gt;Gareth Jones&lt;/a&gt; that will remove the configuration files for packages no longer installed.&lt;/p&gt;
&lt;p&gt;UPDATE [06 Jan 2015]: Raspbian now offers a &lt;a class="reference external" href="https://www.raspberrypi.org/downloads/raspbian/"&gt;"lite" image&lt;/a&gt; that won't include a lot of this bloat. If you're setting up a new Raspberry Pi, consider using that image.&lt;/p&gt;&lt;/div&gt;</description><category>Linux</category><category>Raspberry Pi</category><guid>https://blog.samat.org/2015/02/05/slimming-an-existing-raspbian-install/</guid><pubDate>Thu, 05 Feb 2015 00:00:00 GMT</pubDate></item><item><title>Realtek 8188eu-based Wi-Fi adapters on the Raspberry Pi</title><link>https://blog.samat.org/2014/12/15/realtek-8188eu-based-wi-fi-adapters-on-the-raspberry-pi/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;NOTE: If you're using Raspbian 2015.01 or later, the Raspberry Pi kernel 3.18 now includes this driver—you don't need to compile anything and this tutorial is now irrelevant!&lt;/p&gt;
&lt;p&gt;Raspberry Pi enthusiasts like to use Realtek 8188eu-based Wi-Fi adapters.
Known as "micro" Wi-Fi adapters, they are small and inexpensive.
For example, the &lt;a href="http://www.amazon.com/gp/product/B008IFXQFU/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B008IFXQFU&amp;amp;linkCode=as2&amp;amp;tag=samatjainsper-20&amp;amp;linkId=OEF32Q3XV6SSLWYJ"&gt;TP-LINK WN725N V2&lt;/a&gt; can be found for under $20
(the V1 and V3 use different chipsets.
Thanks for making everything confusing, TP-LINK!).&lt;/p&gt;
&lt;p&gt;As of October 2014 and Linux 3.12.29+, the driver for this Wi-Fi adapter isn't available in the Raspberry Pi Linux kernel, so these Wi-Fi adapters are not supported out-of-the-box, despite otherwise you may find on the Internet.
Definitely, they don't work in a plug-and-play manner. &lt;/p&gt;
&lt;p&gt;You can get these adapters to work by using an out-of-tree driver (i.e. a driver not included with the Linux kernel).
People on the Internet have resorted to doing all these silly things like &lt;a href="http://www.raspberrypi.org/forums/viewtopic.php?p=462982"&gt;downloading random kernel modules&lt;/a&gt; or &lt;a href="http://www.raspberrypi.org/forums/viewtopic.php?f=26&amp;amp;t=29752"&gt;compiling the vendor Linux kernel trees from scratch&lt;/a&gt; trying to get this driver to work. You're probably reading this article because you, like me, thought it was silly to chase files around on some random Internet forum.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/lwfinger/rtl8188eu"&gt;source code for the 8188eu driver&lt;/a&gt; is available on GitHub.
Rather than trying to look for random files and hope they work, you can keep the source for the module on your Raspberry Pi and recompile the module as needed.
Below are copy-and-pastable instructions to do this on Raspbian.&lt;/p&gt;
&lt;p&gt;Make sure you have a compiler, etc installed.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;sudo apt-get install build-essential git
&lt;/pre&gt;


&lt;p&gt;Download the source code for the driver. I prefer to put source code for stuff like this somewhere convenient in my home directory, like ~/src/drivers/.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;mkdir ~/src/drivers
cd ~/src/drivers/
git clone https://github.com/lwfinger/rtl8188eu.git
&lt;/pre&gt;


&lt;p&gt;Now you have the source code for the driver, you need to get the source code for the kernel running on the Raspberry Pi. &lt;a href="http://elinux.org/RPi_Kernel_Compilation"&gt;Finding the source code for the currently-running kernel on the Raspberry Pi can be a pain.&lt;/a&gt; I like to use Hexxeh's rpi-source tool, which you can download, install, and run:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;sudo wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source -O /usr/local/bin/rpi-source &amp;amp;&amp;amp; sudo chmod +x /usr/local/bin/rpi-source &amp;amp;&amp;amp; /usr/local/bin/rpi-source -q --tag-update
sudo rpi-source
&lt;/pre&gt;


&lt;p&gt;If you get an error about GCC, use the "skip-gcc" flag:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;sudo rpi-source --skip-gcc
&lt;/pre&gt;


&lt;p&gt;Now, compile the driver:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;cd ~/src/drivers/rtl8188eu
sudo make all
sudo make install
&lt;/pre&gt;


&lt;p&gt;Your Wi-Fi should be working, though you'll need to configure it (configuring it is out of scope for this article).&lt;/p&gt;
&lt;p&gt;You can double check whether the adapter is recognized by looking at the output of (assuming you've only one Wi-Fi adapter, it will be named wlan0):&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;sudo iwconfig wlan0
&lt;/pre&gt;


&lt;p&gt;If you see nothing, the driver may have been installed wrong or your Wi-Fi adapter not working. 
If you see something like:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;wlan0     unassociated  Nickname:"&amp;lt;WIFI@REALTEK&amp;gt;"
          Mode:Master  Frequency=2.412 GHz  Access Point: E8:DE:27:9E:65:D7   
          Sensitivity:0/0  
          Retry:off   RTS thr:off   Fragment thr:off
          Power Management:off
          Link Quality=0/100  Signal level=0 dBm  Noise level=0 dBm
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:0   Missed beacon:0
&lt;/pre&gt;


&lt;p&gt;Then you are good to go!&lt;/p&gt;
&lt;p&gt;All of this seemed like a lot of work compared to hunting down the file, right?
Except, it isn't: when you update the kernel on your Raspberry Pi and reboot, the Wi-Fi will stop working because it will no longer have an appropriate driver.
Rather than having to go on a witch hunt for files, you can simply recompile the driver again while offline:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;cd ~/src/drivers/rtl8188eu
git pull # Run this if you have Internet access even though your Wi-Fi is down
make all
sudo make install
&lt;/pre&gt;


&lt;p&gt;The above is all you need to turn your Raspberry Pi into a working Wi-Fi client.&lt;/p&gt;
&lt;p&gt;If you want to use your shiny new Wi-Fi enabled Raspberry Pi as an access point, there's a gotcha with Realtek Wi-Fi adapters (I've found this true of both the 8188eu and 8192cu chips): they won't work with hostapd as included with Raspbian or Arch Linux.
You'll need to use a custom hostapd, such as &lt;a href="http://jenssegers.be/blog/43/Realtek-RTL8188-based-access-point-on-Raspberry-Pi"&gt;this one posted by Jens Segers&lt;/a&gt;, to get a working access point.&lt;/p&gt;&lt;/div&gt;</description><category>Linux</category><category>Raspberry Pi</category><category>Wireless</category><guid>https://blog.samat.org/2014/12/15/realtek-8188eu-based-wi-fi-adapters-on-the-raspberry-pi/</guid><pubDate>Mon, 15 Dec 2014 00:00:00 GMT</pubDate></item><item><title>Setting up package caching for pip and npm</title><link>https://blog.samat.org/2014/12/01/setting-up-package-caching-for-pip-and-npm/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;If you're on a metered Internet connection (like me), downloading and re-downloading packages to setup your Python virtualenvs and Node.js projects isn't very fun.
Besides wasting data, it's slow.&lt;/p&gt;
&lt;p&gt;These package managers actually have caching built-in, though it needs to be configured. Follow the instructions below to configure Python's pip, Node.js' NPM, and Twitter's Bower.
I assume you're on Linux and want to use &lt;a href="http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html"&gt;XDG-compliant directories&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Python pip&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://pip.pypa.io/en/latest/reference/pip_install.html#download-cache"&gt;Configuring a cache directory&lt;/a&gt; for pip will cache both source and wheel-based packages.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;cat &amp;gt;&amp;gt; ~/.pip/pip.conf &amp;lt;&amp;lt; EOF
[global]
download_cache = ~/.cache/pip
EOF
mkdir -p ~/.cache/pip
&lt;/pre&gt;


&lt;p&gt;pip's package cache caches things by URL, not by package, which makes pip's caching mechanism not work with mirrors.
The pip manual describes a mechanism for &lt;a href="https://pip.pypa.io/en/latest/user_guide.html#fast-local-installs"&gt;fast and local installs&lt;/a&gt; if this a problem for you.&lt;/p&gt;
&lt;h3&gt;Node.js NPM&lt;/h3&gt;
&lt;p&gt;NPM does cache packages by default, but in a non-XDG compliant directory. Reconfigure it as so:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;npm config set cache ~/.cache/npm
mkdir -p ~/.cache/npm
mv ~/.npm/* ~/.cache/npm/
rm -rf ~/.npm/
&lt;/pre&gt;


&lt;p&gt;See &lt;a href="https://npmjs.org/doc/cache.html"&gt;NPM's documentation for package caching&lt;/a&gt; for further detail.&lt;/p&gt;
&lt;h3&gt;Twitter Bower&lt;/h3&gt;
&lt;p&gt;Bower, like NPM, also caches packages by default in a non-XDG compliant directory. Reconfigure it, and create a .bowerrc:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;bower cache-clean
cat &amp;gt;&amp;gt; ~/.bowerrc &amp;lt;&amp;lt; EOF
{
  "cache": "$HOME/.cache/bower/"
}
EOF
mkdir -p ~/.cache/bower
&lt;/pre&gt;


&lt;p&gt;None of pip, npm, or bower do any purging of outdated or unused packages.
You will have to manage this yourself, unless you are OK of these caches growing without bound (disk space &lt;em&gt;is&lt;/em&gt; cheap these days).&lt;/p&gt;
&lt;p&gt;Attempting to manage these caches is tricky: since modern UNIXes no longer update last accessed times (aka atime) on files, you can't purge packages on last use.
I leave figuring out the best way to purge these directories as an exercise to the reader (please leave a comment!).&lt;/p&gt;&lt;/div&gt;</description><category>Linux</category><category>Node.js</category><category>Python</category><guid>https://blog.samat.org/2014/12/01/setting-up-package-caching-for-pip-and-npm/</guid><pubDate>Mon, 01 Dec 2014 00:00:00 GMT</pubDate></item><item><title>First post!</title><link>https://blog.samat.org/2014/11/25/first-post/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;I'm rebooting this blog. It's a long time coming.&lt;/p&gt;
&lt;p&gt;My first "blog" was back in 2003.
As was fashionable back then, I wrote my own, custom blogging software in PHP.
In 2005, I ditched the custom software and started using Drupal, starting with Drupal 4.5.&lt;/p&gt;
&lt;p&gt;I become fairly skilled with Drupal:
I wrote themes, modules, launched several Websites for myself and clients, ran a Drupal-oriented consulting firm and Web hosting company, and managed scalability at a startup built on Drupal.&lt;/p&gt;
&lt;p&gt;I, very painfully, upgraded those sites and this blog, which started with Drupal 4.5, all the way up to Drupal 7.
It was not easy or fun.&lt;/p&gt;
&lt;p&gt;I had to constantly deal with security upgrades.
Upgrading to new Drupal versions required a witch hunt to find which 3rd party modules were deprecated, which new modules I should use to replace them, and in what ways those modules worked differently.
And then there was all the spam—even with &lt;a class="reference external" href="https://mollom.com/"&gt;Acquia's Mollom&lt;/a&gt; service, too much spam got through, requiring constant moderation.&lt;/p&gt;
&lt;p&gt;The thing that did Drupal in for me was &lt;a class="reference external" href="https://www.drupal.org/SA-CORE-2014-005"&gt;SA-CORE-2014-005&lt;/a&gt;, fixed in Drupal 7.32. After over 10 years of following Drupal security updates, I missed one, by just a few hours—and my site was hacked.
While restoring from my backups, I made a mistake and deleted the most recent one.&lt;/p&gt;
&lt;p&gt;If I wasn't turned off to PHP Web applications before, I'm really, really turned off to them now.
After using Drupal for almost 10 years, I'm switching to something else (at least for my personal stuff).&lt;/p&gt;
&lt;p&gt;Because I didn't want to deal with upgrading or security issues, it meant using something like &lt;a class="reference external" href="https://ghost.org/"&gt;Ghost&lt;/a&gt; or &lt;a class="reference external" href="http://wordpress.org/"&gt;Wordpress&lt;/a&gt; was out.&lt;/p&gt;
&lt;p&gt;The new Samat Says is powered by &lt;a class="reference external" href="http://getnikola.com/"&gt;Nikola&lt;/a&gt;, a static site generator. I looked at &lt;a class="reference external" href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;, &lt;a class="reference external" href="http://blogofile.com/"&gt;Blogofile&lt;/a&gt;, &lt;a class="reference external" href="http://blog.getpelican.com/"&gt;Pelican&lt;/a&gt;, but Nikola seemed the easiest for me to understand, extend, and most importantly has a vibrant, active community.&lt;/p&gt;
&lt;p&gt;For a long time, I cared about owning my comments and not being tied to a 3rd party service.
After dealing with all the spam my old Drupal site received, I now care quite a bit less.
I'd like to move to a self-hosted, or better yet federated, commenting solution in the future but for the time being I'm using &lt;a class="reference external" href="https://www.disquis.com/"&gt;Disqus&lt;/a&gt;.
I haven't figured out how I will migrate old comments yet.&lt;/p&gt;
&lt;p&gt;Worth mentioning: you may have noticed this isn't an actual first post.
I'm slowly migrating content from the &lt;a class="reference external" href="http://oldblog.samat.org/"&gt;old blog&lt;/a&gt; (probably only the popular bits) from my old Drupal installation over to this Nikola-powered one.&lt;/p&gt;&lt;/div&gt;</description><category>Meta</category><guid>https://blog.samat.org/2014/11/25/first-post/</guid><pubDate>Tue, 25 Nov 2014 00:00:00 GMT</pubDate></item><item><title>Weekend irony with the University of Florida</title><link>https://blog.samat.org/2013/04/23/weekend-irony-with-the-university-of-florida/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;p&gt;Irony: this weekend (Apr 21–22), NPR’s &lt;a href="http://www.npr.org/programs/wait-wait-dont-tell-me/"&gt;Wait Wait˙ Don’t Tell me!&lt;/a&gt; re-ran a &lt;a href="http://www.npr.org/templates/rundowns/rundown.php?prgId=35&amp;amp;prgDate=04-21-2012"&gt;segment with author Jack Gantos&lt;/a&gt;. In it, Jack Gantos makes a crack at the University of Florida:&lt;/p&gt;
&lt;blockquote&gt;I drove up to University of Florida. It looked just like my high school — a giant football facility with a small academic institution
&lt;/blockquote&gt;

&lt;p&gt;Apparently, University of Florida thought this was a compliment. Forbes reports (&lt;a href="http://identi.ca/notice/92981526"&gt;via&lt;/a&gt;) that &lt;a href="http://www.forbes.com/sites/stevensalzberg/2012/04/22/university-of-florida-eliminates-computer-science-department-increases-athletic-budgets-hmm/"&gt;University of Florida has eliminated research &amp;amp; graduate work in its Computer Science department&lt;/a&gt;, while simultaneously significantly increasing funding for athletics.&lt;/p&gt;
&lt;p&gt;A clarification missing from the Forbes article: &lt;a href="http://computinged.wordpress.com/2012/04/13/university-of-florida-to-dismantle-cise-department/"&gt;UF is eliminating research and graduate work&lt;/a&gt; in its computer science department, just like it did for its nuclear engineering department the year prior. The departments will remain as severely gimped teaching-only undergraduate departments… not unlike a glorified community college.&lt;/p&gt;
&lt;p&gt;Keep it real University of Florida!&lt;/p&gt;&lt;/div&gt;</description><category>News</category><guid>https://blog.samat.org/2013/04/23/weekend-irony-with-the-university-of-florida/</guid><pubDate>Tue, 23 Apr 2013 00:00:00 GMT</pubDate></item><item><title>Spaceport America on OpenStreetMap</title><link>https://blog.samat.org/2012/01/11/Spaceport-America-on-OpenStreetMap/</link><dc:creator>Samat K Jain</dc:creator><description>&lt;div&gt;&lt;figure class="right"&gt;[flickr-photo:id=6639706995,size=m]&lt;/figure&gt;

&lt;p&gt;&lt;a href="http://www.spaceportamerica.com/"&gt;Spaceport America&lt;/a&gt; is "the world's first purpose-built commercial spaceport". Wonder what it looks like? You can now &lt;a href="http://www.openstreetmap.org/?lat=32.9891&amp;amp;lon=-106.97534&amp;amp;zoom=15&amp;amp;layers=M"&gt;find it on OpenStreetMap&lt;/a&gt;, one of the many things I've been mapping in New Mexico's barren &amp;amp; isolated &lt;a href="http://en.wikipedia.org/wiki/Jornada_del_Muerto"&gt;Jornada del Muerto&lt;/a&gt;. I've indicated various Spaceport America structures, like the state-of-the-art Terminal Hangar Building and Spaceport Operations Center. I've yet to accurately locate Spaceport America's vertical launch pad, which has been in use since 2007.&lt;/p&gt;
&lt;p&gt;No, it's not on Bing Maps, Google Maps, or any of the other Web mapping competitors—just in case you needed a reason why crowd-sourced geodata (or &lt;abbr title="Volunteered Geographic Information"&gt;VGI&lt;/abbr&gt;) can't be beat. &lt;/p&gt;
&lt;p&gt;Want an &lt;a href="http://www.flickr.com/photos/tamasrepus/6639706995/"&gt;aerial photo of Spaceport America&lt;/a&gt;? Over on Flickr I've a screencap from the USDA's public-domain &lt;abbr title="National Aerial Imagery Program"&gt;&lt;a href="http://en.wikipedia.org/wiki/National_Agriculture_Imagery_Program"&gt;NAIP&lt;/a&gt;&lt;/abbr&gt; 2011 release, pretty much the only source for high-resolution imagery of the middle of nowhere.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.openstreetmap.org/?lat=32.9891&amp;amp;lon=-106.97534&amp;amp;zoom=15&amp;amp;layers=M" class="call-to-action"&gt;See Spaceport America on OpenStreetMap&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><category>Needs-Fixing</category><category>New Mexico</category><category>OpenStreetMap</category><guid>https://blog.samat.org/2012/01/11/Spaceport-America-on-OpenStreetMap/</guid><pubDate>Wed, 11 Jan 2012 00:00:00 GMT</pubDate></item></channel></rss>