<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Not sorry for the inconvenience</title>
 <link href="https://kynan.github.io/atom.xml" rel="self"/>
 <link href="https://kynan.github.io/"/>
 <updated>2026-04-12T13:07:00+00:00</updated>
 <id>https://kynan.github.io</id>
 <author>
   <name>Florian Rathgeber</name>
   <email>florian.rathgeber@gmail.com</email>
 </author>

 
   
   <entry>
     <title>Save all voicebox messages from your FRITZ!Box 7530 router</title>
     <link href="https://kynan.github.io/blog/2023/12/28/save-all-voicebox-messages-from-your-fritzbox"/>
     <updated>2023-12-28T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2023/12/28/save-all-voicebox-messages-from-your-fritzbox</id>
     <content type="html">&lt;p&gt;My parents’ FRITZ!Box 7530 (FRITZ!OS version 7.57) voicebox had “filled up” and before clearing all messages I was looking for a way to download all of them as a backup.&lt;/p&gt;

&lt;p&gt;There is no really convenient way to do this in the web UI (there were close to 200 messages, some dating back more than 4 years), so I resorted to JavaScript to print a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; commands to download each message as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.wav&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Messages are listed in a table with the following format:&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;thead&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sortable&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sortable sort_by_date&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Date&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sort_no&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sortable&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Name/Number&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sort_no&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sortable&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Your Number&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sort_no&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sortable&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Duration&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sort_no&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;newicon&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;datalabel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;28.12.23 16:00&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;28.12.23 16:00&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;datalabel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Name/Number&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Caller&apos;s Name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;datalabel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Your Number&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;1234&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;datalabel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Dauer&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ni&quot;&gt;&amp;amp;lt;&lt;/span&gt; 1 Min&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;btncolumn&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;datalabel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;icon fon_book&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;disabled=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/assets/icons/ic_fonbook_add.svg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;download icon&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/cgi-bin/luacgi_notimeout?sid=&amp;lt;session uuid&amp;gt;&amp;amp;amp;script=%2Flua%2Fphoto.lua&amp;amp;amp;myabfile=%2Fdata%2Ftam%2Frec%2F&amp;lt;recording id&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;icon audio&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;play_1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;play&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Play message/Save&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/assets/icons/ic_triangle_right_blue.svg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Play message/Save&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;audio&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;preload=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;auto&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/audio&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;icon delete&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;delete_1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;delete&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Delete&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We want to ignore the header row and for every subsequent row, get the date and the caller’s name or number, to build a meaningful file name for the to-be-downloaded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.wav&lt;/code&gt; file, and the URL of the link. The link points to a script that emits the raw WAV audio as a binary stream.&lt;/p&gt;

&lt;p&gt;This is a quick &amp;amp; dirty piece of JavaScript to paste into the JavaScript console of Chrome’s Developer tools (keyboard shortcut Ctrl-Shift-I) to generate a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; invocations to download each message with a useful file name:&lt;/p&gt;
&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;table#uiTamCalls tr:not(.thead)&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`curl -s -o &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;childNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;childNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.wav&quot; &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My initial attempt was downloading the file via the browser by invoking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.click()&lt;/code&gt; on each link, however that turned out to work less well as Chrome doesn’t really like downloading files over a non-secure connection, requires you to allow multiple simultaneous downloads via a popup box and even if you do, will only download 10 files at a time.&lt;/p&gt;

&lt;p&gt;YMMV if you have a different FRITZ!Box model or FRITZ!OS version, however I’d expect a similar approach should still work.&lt;/p&gt;
</content>
   </entry>
   
 
   
   <entry>
     <title>Commands to replace the deprecated `apt-key` script</title>
     <link href="https://kynan.github.io/blog/2021/01/01/commands-to-replace-the-deprecated-apt-key-script"/>
     <updated>2021-01-01T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2021/01/01/commands-to-replace-the-deprecated-apt-key-script</id>
     <content type="html">&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-key&lt;/code&gt; is &lt;a href=&quot;https://manpages.debian.org/testing/apt/apt-key.8.en.html&quot;&gt;deprecated&lt;/a&gt;,
however there is no replacement available, neither does the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man&lt;/code&gt; page document
how to replace the commands &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-key&lt;/code&gt; provides. Here is my attempt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg&lt;/code&gt; will by default create new keyrings in the (new) “GPG keybox
database version 1”, whereas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; expects them in the (legacy) “PGP/GPG key
public ring (v4)” format. Specify the prefix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gnupg-ring:&lt;/code&gt; for the keyring file
to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg&lt;/code&gt; use the legacy v4 format.&lt;/p&gt;

&lt;h2 id=&quot;apt-key-list&quot;&gt;apt-key list&lt;/h2&gt;

&lt;p&gt;This commands lists all keys stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/apt/trusted.gpg&lt;/code&gt; and any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gpg&lt;/code&gt; or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.asc&lt;/code&gt; files in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/apt/trusted.gpg.d&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /etc/apt/trusted.gpg /etc/apt/trusted.gpg.d/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;asc,gpg&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--list-keys&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--keyid-format&lt;/span&gt; short &lt;span class=&quot;nt&quot;&gt;--no-default-keyring&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--keyring&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;apt-key-adv&quot;&gt;apt-key adv&lt;/h2&gt;

&lt;p&gt;This command is used to download a key and store it in the “right” keyring.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-key adv&lt;/code&gt; merges all keyrings into one, downloads the new key(s) and then
merges back the changes. No need to replicate this setup.&lt;/p&gt;

&lt;h3 id=&quot;updating-an-expired-key&quot;&gt;Updating an expired key&lt;/h3&gt;

&lt;p&gt;If you’re updating an expired key, write it to the same keyring, replacing the
expired key. To find any keyrings containing an expired key, run the following:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /etc/apt/trusted.gpg /etc/apt/trusted.gpg.d/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;asc,gpg&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--list-keys&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-default-keyring&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--keyring&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt; | fgrep &lt;span class=&quot;nt&quot;&gt;-iq&lt;/span&gt; expired&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Expired key in &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you’ve identified the keyring and key ID, download the new key:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--recv-keys&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-default-keyring&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--keyring&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;gnupg-ring:/etc/apt/trusted.gpg.d/&amp;lt;FILENAME&amp;gt;.gpg &lt;span class=&quot;nt&quot;&gt;--keyserver&lt;/span&gt; keys.gnupg.net &amp;lt;KEY_ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;downloading-a-new-key&quot;&gt;Downloading a new key&lt;/h3&gt;

&lt;p&gt;When downloading a new key, create a new keyring in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/apt/trusted.gpg.d&lt;/code&gt;.
Note that on recent versions of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg&lt;/code&gt;, this keyring will be in “GPG keybox
database version 1” format, which is incompatible with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Choose a suitable filename for the new keyring and download the key:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--recv-keys&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-default-keyring&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--keyring&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;gnupg-ring:/etc/apt/trusted.gpg.d/&amp;lt;FILENAME&amp;gt;.kbx &lt;span class=&quot;nt&quot;&gt;--keyserver&lt;/span&gt; keys.gnupg.net &amp;lt;KEY_ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
   </entry>
   
 
   
   <entry>
     <title>How to create a transcript of a Google Support chat</title>
     <link href="https://kynan.github.io/blog/2020/12/30/how-to-create-a-transcript-for-a-google-support-chat"/>
     <updated>2020-12-30T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2020/12/30/how-to-create-a-transcript-for-a-google-support-chat</id>
     <content type="html">&lt;p&gt;When you interact with Google Support via chat, you can ask for a transcript of
the conversation to be sent to you. That transcript is in PDF format though. If
that’s not suitable for you or you forgot to request a transcript, there is a
way out.&lt;/p&gt;

&lt;p&gt;Open the chat in its own pop-out window via the arrow icon on the right of the
blue header bar. Then open the Chrome developer console
(&lt;kbd&gt;Ctrl&lt;/kbd&gt;-&lt;kbd&gt;Shift&lt;/kbd&gt;-&lt;kbd&gt;J&lt;/kbd&gt;), paste the following JavaScript
code and hit enter to copy a transcript of the conversation to your clipboard.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;copyToClipboard&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;textarea&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;execCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;removeChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;copyToClipboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.chatsupport_cbf_qb&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toISOString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.systemMessageUserWrapper&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.systemMessageUserWrapper&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.chatsupport_cbf_rb&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;aria-label&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.chatsupport_cbf_ob&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;]: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
   </entry>
   
 
   
   <entry>
     <title>How to upload your package to the Python Package Index (PyPI) test server</title>
     <link href="https://kynan.github.io/blog/2020/05/23/how-to-upload-your-package-to-the-python-package-index-pypi-test-server"/>
     <updated>2020-05-23T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2020/05/23/how-to-upload-your-package-to-the-python-package-index-pypi-test-server</id>
     <content type="html">&lt;p&gt;Python packages are distributed via the Python Package Index (&lt;a href=&quot;https://pypi.org&quot;&gt;PyPI&lt;/a&gt;). The
&lt;a href=&quot;https://packaging.python.org&quot;&gt;Python Packaging Guide&lt;/a&gt; provides details for &lt;a href=&quot;https://packaging.python.org/guides/distributing-packages-using-setuptools/#uploading-your-project-to-pypi&quot;&gt;uploading your project to PyPI&lt;/a&gt;.
However the packaging guide is missing instructions for uploading to the
&lt;a href=&quot;https://test.pypi.org&quot;&gt;PyPI test server&lt;/a&gt;. This is &lt;a href=&quot;https://packaging.python.org/guides/using-testpypi/&quot;&gt;recommended&lt;/a&gt; as a trial run for uploading a new
version of your package, since you upload a given version of your project only
&lt;em&gt;once&lt;/em&gt;. You can use the test server to e.g. verify your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README&lt;/code&gt; renders
correctly.&lt;/p&gt;

&lt;p&gt;Assuming you have finished all the work on the new release of your project,
written the &lt;a href=&quot;https://keepachangelog.com&quot;&gt;release notes&lt;/a&gt;, &lt;a href=&quot;https://pypi.org/project/bump2version/&quot;&gt;increased the version number&lt;/a&gt;, tagged the release
and are ready to publish, follow theses steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;If you haven’t yet, create accounts on &lt;a href=&quot;https://pypi.org&quot;&gt;PyPI&lt;/a&gt; and the &lt;a href=&quot;https://test.pypi.org&quot;&gt;PyPI test server&lt;/a&gt;.
Note that these accounts are entirely independent, however you may want to
use the same username (but different passwords, of course).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For security reasons it is strongly recommended to create an &lt;a href=&quot;https://pypi.org/help/#apitoken&quot;&gt;API token&lt;/a&gt;
instead of using your username and password when uploading a package to
&lt;a href=&quot;https://pypi.org&quot;&gt;PyPI&lt;/a&gt;. If you haven’t done so, create an &lt;a href=&quot;https://pypi.org/help/#apitoken&quot;&gt;API token&lt;/a&gt; on both &lt;a href=&quot;https://pypi.org/manage/account/token/&quot;&gt;PyPI&lt;/a&gt; and
the &lt;a href=&quot;https://test.pypi.org/manage/account/token/&quot;&gt;PyPI test server&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;You can choose to restrict this token to only a single package, which you
should definitely do if you use the &lt;a href=&quot;https://pypi.org/help/#apitoken&quot;&gt;API token&lt;/a&gt; e.g. in a CI/CD workflow.
For your personal use I suggest you leave the token unrestricted, since
&lt;a href=&quot;https://github.com/pypa/twine/issues/496&quot;&gt;there is no good workflow for switching between multiple API tokens&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;Note that the &lt;a href=&quot;https://pypi.org/help/#apitoken&quot;&gt;API token&lt;/a&gt; will only be displayed once when you create it,
so make sure you copy the token. If you forget to do that, revoke and
create a new one.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pypirc&lt;/code&gt; file in your home directory to store your API tokens
for authentication when uploading, with the following content:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[pypi]
username = __token__
password = pypi-AgEIcH...

[testpypi]
username = __token__
password = pypi-AgENdG...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you haven’t done so, install &lt;a href=&quot;https://twine.readthedocs.io&quot;&gt;twine&lt;/a&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install --upgrade twine&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create a &lt;a href=&quot;https://packaging.python.org/glossary/#term-source-distribution-or-sdist&quot;&gt;source distribution&lt;/a&gt; and a &lt;a href=&quot;https://packaging.python.org/guides/distributing-packages-using-setuptools/#wheels&quot;&gt;wheel&lt;/a&gt; for your package:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python setup.py sdist bdist_wheel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twine check&lt;/code&gt; on your package files and ensure they pass:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ twine check dist/*
Checking dist/dokuwikixmlrpc-2020.5.23-py2.py3-none-any.whl: PASSED
Checking dist/dokuwikixmlrpc-2020.5.23.tar.gz: PASSED
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This command will report any problems rendering your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Upload your packages to the &lt;a href=&quot;https://test.pypi.org&quot;&gt;PyPI test server&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;twine upload --repository testpypi dist/*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;You should not be prompted for a username or password, since those are
configure in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pypirc&lt;/code&gt;. When successful, this should print the URL of
your package on the test server:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Uploading distributions to https://test.pypi.org/legacy/
Uploading dokuwikixmlrpc-2020.5.23-py2.py3-none-any.whl
100%|████████████████████████████| 10.1k/10.1k [00:01&amp;lt;00:00, 5.23kB/s]
Uploading dokuwikixmlrpc-2020.5.23.tar.gz
100%|████████████████████████████| 9.63k/9.63k [00:01&amp;lt;00:00, 9.39kB/s]

View at:
https://test.pypi.org/project/dokuwikixmlrpc/2020.5.23/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Verify everything is as you expect e.g. there are no rendering errors.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Upload your packages to &lt;a href=&quot;https://pypi.org&quot;&gt;PyPI&lt;/a&gt;, fo realz:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;twine upload dist/*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;You should not be prompted for a username or password, since those are
configure in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pypirc&lt;/code&gt;. When successful, you should see output similar
to the above.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Congratulations! You’ve done it! You package is now available to the world 🎉&lt;/p&gt;

</content>
   </entry>
   
 
   
   <entry>
     <title>Analysing Parliamentary Questions Answered at Accountability Hack 2015</title>
     <link href="https://kynan.github.io/blog/2015/11/22/Accountability-Hack-2015"/>
     <updated>2015-11-22T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2015/11/22/Accountability-Hack-2015</id>
     <content type="html">&lt;p&gt;A very frosty November weekend marked the end of &lt;a href=&quot;https://parliamentweek.org&quot;&gt;Parliament Week&lt;/a&gt; and the
fifth anniversary of the &lt;a href=&quot;http://accountabilityhack.org&quot;&gt;Accountability Hack&lt;/a&gt;, originally named UK Parliament
Hack, organised by &lt;a href=&quot;https://twitter.com/greentrac&quot;&gt;Tracy Green&lt;/a&gt; from the Parliament Digital Service, &lt;a href=&quot;http://nickmhalliday.net&quot;&gt;Nick
Halliday&lt;/a&gt; from the &lt;a href=&quot;http://nao.org.uk&quot;&gt;National Audit Office&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/TerryMakewell&quot;&gt;Terry Makewell&lt;/a&gt; from the
&lt;a href=&quot;http://ons.gov.uk&quot;&gt;Office for National Statistics&lt;/a&gt; with very active support from the
&lt;a href=&quot;http://rebeluncut.co.uk&quot;&gt;RebelUncut&lt;/a&gt; crew.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Hack &lt;a href=&quot;https://twitter.com/hashtag/Parliament?src=hash&quot;&gt;#Parliament&lt;/a&gt;&amp;#39;s data and show us what you can do with it, sign up to &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; &lt;a href=&quot;https://t.co/mcN02u4NbV&quot;&gt;https://t.co/mcN02u4NbV&lt;/a&gt;&lt;/p&gt;&amp;mdash; UK Parliament (@UKParliament) &lt;a href=&quot;https://twitter.com/UKParliament/status/665273451368873984&quot;&gt;November 13, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;Hackers and “armchair auditors” were invited to tackle four different
challenges using a diverse set of &lt;a href=&quot;http://accountabilityhack.org/#data&quot;&gt;open data sources&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;NAO&lt;/strong&gt;: Use spend data and any other data set to improve accountability.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Parliament&lt;/strong&gt;: Best use of linked data to improve accountability.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ONS&lt;/strong&gt;: Use the ONS OpenAPI to improve accountability.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Wildcard&lt;/strong&gt;: Use any three open data sets to improve accountability.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;And we&amp;#39;re LIVE! &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; is go &lt;a href=&quot;https://t.co/QHjV7QWOUd&quot;&gt;pic.twitter.com/QHjV7QWOUd&lt;/a&gt;&lt;/p&gt;&amp;mdash; RebelUncut (@RebelUncut) &lt;a href=&quot;https://twitter.com/RebelUncut/status/667991291893010432&quot;&gt;November 21, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;Many prospective participants were deterred by either the freezing cold or by
issues with public transport, so not that many heard &lt;a href=&quot;https://twitter.com/Meg_HillierMP&quot;&gt;Meg Hillier MP&lt;/a&gt; give the
introductory address. After that, ideas were thrown around and teams started
forming. I joined &lt;a href=&quot;https://twitter.com/NataliaLKB&quot;&gt;Natalia&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/minaorangina&quot;&gt;Mina&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/emjan29&quot;&gt;Emma&lt;/a&gt;, a brilliant trio who were
working on a visualisation of &lt;a href=&quot;http://explore.data.parliament.uk/?learnmore=Parliamentary Questions Answered&quot;&gt;Parliamentary Questions Answered&lt;/a&gt; and were
looking for some help with crunching the data and classifying the quality of
answers.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Wonderful to have &lt;a href=&quot;https://twitter.com/Meg_HillierMP&quot;&gt;@Meg_HillierMP&lt;/a&gt; sharing her passion for holding &amp;quot;people like me&amp;quot; accountable &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; &lt;a href=&quot;https://t.co/q98Q85HkPq&quot;&gt;pic.twitter.com/q98Q85HkPq&lt;/a&gt;&lt;/p&gt;&amp;mdash; RebelUncut (@RebelUncut) &lt;a href=&quot;https://twitter.com/RebelUncut/status/668010032991223808&quot;&gt;November 21, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;We first of all needed to pull all data from the Parliament’s &lt;a href=&quot;http://lda.data.parliament.uk/answeredquestions&quot;&gt;Linked Data
API&lt;/a&gt; in JSON format. Downloading all 63000 questions in batches of 500 (which
is the maximum batch size the API allows unfortunately) by hand was of course
not an option, so I started by implementing a download script in Python.
Pulling down all questions took several hours due to the rather poor
performance of the API.&lt;/p&gt;

&lt;p&gt;In the evening, &lt;a href=&quot;http://lws.io&quot;&gt;Kevin&lt;/a&gt; ran a very entertaining round of the &lt;a href=&quot;http://no-light.mlh.io&quot;&gt;MLH !LIGHT&lt;/a&gt;
challenge, where each contestant has 15 minutes to re-create a given website
(in our case it was the &lt;a href=&quot;http://getbootstrap.com&quot;&gt;bootstrap&lt;/a&gt; front page) using a very bare bones
browser based editor with no syntax highlighting or auto completion. No
navigating away from the tab to bring up help and you don’t get to see a
rendered preview of your creation until after you submitted.&lt;/p&gt;

&lt;p&gt;The overnight stay at the NAO was again quite comfortable and we could use a
shower in the morning. My team from the Saturday did sadly not come back,
however &lt;a href=&quot;http://artofinference.com&quot;&gt;John Sandall&lt;/a&gt;, a good friend and brilliant data scientist, arrived in
time for breakfast. We discussed classifying answer quality with an N-gram
analysis using a list of previously identified phrases commonly used to defer
questions. An alternative would be training a text analysis model on the
entire text corpus based on a training set of manually classified answers.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://twitter.com/emjan29&quot;&gt;@emjan29&lt;/a&gt; &lt;a href=&quot;https://twitter.com/minaorangina&quot;&gt;@minaorangina&lt;/a&gt; &lt;a href=&quot;https://twitter.com/NataliaLKB&quot;&gt;@NataliaLKB&lt;/a&gt; I’m working with &lt;a href=&quot;https://twitter.com/frathgeber&quot;&gt;@frathgeber&lt;/a&gt; at &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; we have a big flat CSV of MPs Q&amp;amp;A, you coming back for today?&lt;/p&gt;&amp;mdash; John Sandall (@John_Sandall) &lt;a href=&quot;https://twitter.com/John_Sandall/status/668401018288349184&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I’m at Accountability Hack at the &lt;a href=&quot;https://twitter.com/NAOorguk&quot;&gt;@NAOorguk&lt;/a&gt; today &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; using &lt;a href=&quot;https://twitter.com/UKParliData&quot;&gt;@ukparlidata&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/data?src=hash&quot;&gt;#data&lt;/a&gt; to hold lawmakers to account! &lt;a href=&quot;https://t.co/JKCQgzN33C&quot;&gt;pic.twitter.com/JKCQgzN33C&lt;/a&gt;&lt;/p&gt;&amp;mdash; John Sandall (@John_Sandall) &lt;a href=&quot;https://twitter.com/John_Sandall/status/668403230745980928&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;Before getting to work on that we needed to transform the raw data into a
suitable form and identify which attributes were relevant for our analysis.
Halfway through doing that I realised the answer text was missing from the
data and found out it was due to passing a query parameter to the API
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_view=all&lt;/code&gt;), which included extra fields, but left out the actual answer
data. By that point it was unrealistic to be able to rerun the entire download
in time for show &amp;amp; tell.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Need to pull all data from &lt;a href=&quot;https://twitter.com/UKParliData&quot;&gt;@UKParliData&lt;/a&gt; again, because ?_view=all contrary to what you might think does *not* give you everything &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt;&lt;/p&gt;&amp;mdash; Florian Rathgeber (@frathgeber) &lt;a href=&quot;https://twitter.com/frathgeber/status/668432437370880000&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Full MPs answered questions &lt;a href=&quot;https://twitter.com/hashtag/OpenData?src=hash&quot;&gt;#OpenData&lt;/a&gt; from &lt;a href=&quot;https://twitter.com/UKParliData&quot;&gt;@ukparlidata&lt;/a&gt; as 62k row CSV (7MB compressed) -&amp;gt; &lt;a href=&quot;https://t.co/JmuqMdl3k9&quot;&gt;https://t.co/JmuqMdl3k9&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt;&lt;/p&gt;&amp;mdash; John Sandall (@John_Sandall) &lt;a href=&quot;https://twitter.com/John_Sandall/status/668408168356139009&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;John did however still manage to run some statistical analysis on the data to
answer many interesting questions. Meanwhile I turned my download script into
a “proper” Python package using &lt;a href=&quot;https://pyscaffold.readthedocs.org&quot;&gt;PyScaffold&lt;/a&gt;, uploaded the package to &lt;a href=&quot;https://pypi.python.org/pypi/ddpuk&quot;&gt;PyPI&lt;/a&gt;
and the &lt;a href=&quot;https://ddpy.readthedocs.org&quot;&gt;documentation&lt;/a&gt; to &lt;a href=&quot;https://readthedocs.org&quot;&gt;Read the Docs&lt;/a&gt; - just in time for going up on
stage!&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Here we go!! &lt;a href=&quot;https://twitter.com/gabysslave&quot;&gt;@gabysslave&lt;/a&gt; kicking off &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; show &amp;amp; tell &lt;a href=&quot;https://t.co/kkTCNOZ5jG&quot;&gt;pic.twitter.com/kkTCNOZ5jG&lt;/a&gt;&lt;/p&gt;&amp;mdash; RebelUncut (@RebelUncut) &lt;a href=&quot;https://twitter.com/RebelUncut/status/668447489339564033&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Sounds like we&amp;#39;re going to get some robust feedback on &lt;a href=&quot;https://twitter.com/UKParliData&quot;&gt;@UKParliData&lt;/a&gt; now... &lt;a href=&quot;https://twitter.com/hashtag/acchack15?src=hash&quot;&gt;#acchack15&lt;/a&gt;&lt;/p&gt;&amp;mdash; Dan Barrett (@dasbarrett) &lt;a href=&quot;https://twitter.com/dasbarrett/status/668459325011177472&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;Quite a few extra spectators came along to attend the show &amp;amp; tell with a
rather impressive lineup of 16 projects! I was on stage twice: First to
present the results of our analysis of “Any Questions Answered?”, which had
revealed some interesting &lt;a href=&quot;https://github.com/john-sandall/acchack2015/blob/master/presentation/AccHack%20presentation.pdf&quot;&gt;insights&lt;/a&gt;. &lt;a href=&quot;https://twitter.com/JimShannonMP&quot;&gt;Jim Shannon MP&lt;/a&gt; asked the most questions,
the &lt;a href=&quot;https://www.gov.uk/government/organisations/department-of-health&quot;&gt;Department of Health&lt;/a&gt; had to answer the most. The &lt;a href=&quot;https://www.gov.uk/government/organisations/foreign-commonwealth-office&quot;&gt;Foreign &amp;amp; Commonwealth
Office&lt;/a&gt; was the slowest to respond. &lt;a href=&quot;https://twitter.com/nick_clegg&quot;&gt;Nick Clegg&lt;/a&gt;’s questions were ignored the
longest and the Prime Minister referred the highest proportion of questions.
With more time to build up a training set by categorising some questions
manually e.g. for quality or difficulty, we could have trained a Bayes
classifier for the entire corpus.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://twitter.com/hashtag/acchack15?src=hash&quot;&gt;#acchack15&lt;/a&gt; next up any questions answered... &lt;a href=&quot;https://t.co/4KaEJHr64m&quot;&gt;pic.twitter.com/4KaEJHr64m&lt;/a&gt;&lt;/p&gt;&amp;mdash; Ranjan Balakumaran (@financialeyes) &lt;a href=&quot;https://twitter.com/financialeyes/status/668459577038520320&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Fun fact: &lt;a href=&quot;https://twitter.com/nick_clegg&quot;&gt;@nick_clegg&lt;/a&gt; ranks higher than any other MP for how long it takes to get questions answered (49 days on avg) &lt;a href=&quot;https://twitter.com/hashtag/PoorNick?src=hash&quot;&gt;#PoorNick&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt;&lt;/p&gt;&amp;mdash; John Sandall (@John_Sandall) &lt;a href=&quot;https://twitter.com/John_Sandall/status/668428101609775104&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&amp;quot;Have all these MPs always been so annoying?!&amp;quot; &lt;a href=&quot;https://twitter.com/John_Sandall&quot;&gt;@John_Sandall&lt;/a&gt; Love it &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt;&lt;/p&gt;&amp;mdash; RebelUncut (@RebelUncut) &lt;a href=&quot;https://twitter.com/RebelUncut/status/668459719363833856&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;Later I went on stage again to present &lt;a href=&quot;https://github.com/kynan/DDPy&quot;&gt;DDPy&lt;/a&gt;, a command line interface to
interact with the Parliament &lt;a href=&quot;http://lda.data.parliament.uk/answeredquestions&quot;&gt;Linked Data API&lt;/a&gt;, which had evolved out of my
download script to pull the &lt;a href=&quot;http://explore.data.parliament.uk/?learnmore=Parliamentary Questions Answered&quot;&gt;Parliamentary Questions Answered&lt;/a&gt;. I decided to
solve this once and for all and, wrote a generic downloader in Python and put
it on &lt;a href=&quot;https://pypi.python.org/pypi/ddpuk&quot;&gt;PyPI&lt;/a&gt;. Now anyone can easily download any data set after a simple&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install ddpkuk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Ok so *now* &lt;a href=&quot;https://twitter.com/frathgeber&quot;&gt;@frathgeber&lt;/a&gt; is going to give us some robust feedback on &lt;a href=&quot;https://twitter.com/UKParliData&quot;&gt;@ukparlidata&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/acchack15?src=hash&quot;&gt;#acchack15&lt;/a&gt;&lt;/p&gt;&amp;mdash; Dan Barrett (@dasbarrett) &lt;a href=&quot;https://twitter.com/dasbarrett/status/668469540301496322&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;DDPy from &lt;a href=&quot;https://twitter.com/frathgeber&quot;&gt;@frathgeber&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; &lt;a href=&quot;https://t.co/YeBULBhRrt&quot;&gt;https://t.co/YeBULBhRrt&lt;/a&gt;&lt;/p&gt;&amp;mdash; RebelUncut (@RebelUncut) &lt;a href=&quot;https://twitter.com/RebelUncut/status/668470085678444544&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Read how to use DDPy to access &lt;a href=&quot;https://twitter.com/UKParliData&quot;&gt;@UKParliData&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/data?src=hash&quot;&gt;#data&lt;/a&gt; at &lt;a href=&quot;https://t.co/iJTmrdEv89&quot;&gt;https://t.co/iJTmrdEv89&lt;/a&gt; and get the code on &lt;a href=&quot;https://twitter.com/github&quot;&gt;@GitHub&lt;/a&gt; &lt;a href=&quot;https://t.co/ufLZdFP0pk&quot;&gt;https://t.co/ufLZdFP0pk&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt;&lt;/p&gt;&amp;mdash; Florian Rathgeber (@frathgeber) &lt;a href=&quot;https://twitter.com/frathgeber/status/668473642708246528&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;The judges apparently came away quite impressed with both our presentations
since we received an honourable mention for the “Best Analysis of Parliamentary
Data” for &lt;a href=&quot;https://github.com/john-sandall/acchack2015/blob/master/presentation/AccHack%20presentation.pdf&quot;&gt;Any Questions Answered?&lt;/a&gt; and the “Best Tool for the
Community” for &lt;a href=&quot;https://github.com/kynan/DDPy&quot;&gt;DDPy&lt;/a&gt;. As if that wasn’t enough, I was quite touched for also
being awarded a “Community Spirit Prize”. It was (and continues to be) an
honour and pleasure serving the community!&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Got an honourable mention at &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; for &amp;quot;Any Question Answered?&amp;quot; our analysis of Parliamentary Questions Answered /w &lt;a href=&quot;https://twitter.com/John_Sandall&quot;&gt;@John_Sandall&lt;/a&gt;&lt;/p&gt;&amp;mdash; Florian Rathgeber (@frathgeber) &lt;a href=&quot;https://twitter.com/frathgeber/status/668486316645081088&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Got another honourable mention at &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; for DDPy as a useful tool for the &lt;a href=&quot;https://twitter.com/hashtag/hackathon?src=hash&quot;&gt;#hackathon&lt;/a&gt; community. So go &amp;amp; use it! &lt;a href=&quot;https://t.co/80K4puh9Wh&quot;&gt;https://t.co/80K4puh9Wh&lt;/a&gt;&lt;/p&gt;&amp;mdash; Florian Rathgeber (@frathgeber) &lt;a href=&quot;https://twitter.com/frathgeber/status/668486769495695360&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;No end of awards today it seems ;)&amp;#10;Many thanks to &lt;a href=&quot;https://twitter.com/RebelUncut&quot;&gt;@RebelUncut&lt;/a&gt; &lt;a href=&quot;https://twitter.com/nickmhalliday&quot;&gt;@nickmhalliday&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/greentrac&quot;&gt;@greentrac&lt;/a&gt; for running &lt;a href=&quot;https://twitter.com/hashtag/AccHack15?src=hash&quot;&gt;#AccHack15&lt;/a&gt; &lt;a href=&quot;https://t.co/9hvSrkubyY&quot;&gt;https://t.co/9hvSrkubyY&lt;/a&gt;&lt;/p&gt;&amp;mdash; Florian Rathgeber (@frathgeber) &lt;a href=&quot;https://twitter.com/frathgeber/status/668487299320176640&quot;&gt;November 22, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;h1 id=&quot;any-questions-answered&quot;&gt;Any Questions Answered?&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/john-sandall/acchack2015&quot;&gt;Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/john-sandall/acchack2015/blob/master/presentation/AccHack%20presentation.pdf&quot;&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;ddpy---dataparliamentuk-for-humans&quot;&gt;DDPy - data.parliament.uk for Humans&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/kynan/DDPy&quot;&gt;Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ddpy.readthedocs.org&quot;&gt;Documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pypi.python.org/pypi/ddpuk&quot;&gt;Python Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;other-resources&quot;&gt;Other resources&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://bit.ly/AccHackpad15&quot;&gt;Accountability Hack 2015 hackpad&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
   </entry>
   
 
   
   <entry>
     <title>Chilterns circular | Reading - Nuffield - Chinnor - West Wycombe - Marlow - Reading</title>
     <link href="https://kynan.github.io/blog/2015/09/06/chilterns-circular-reading-chinnor-marlow"/>
     <updated>2015-09-06T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2015/09/06/chilterns-circular-reading-chinnor-marlow</id>
     <content type="html">&lt;p&gt;With a warm sunny day forecast for Sunday - potentially the last summer day -
it was another perfect opportunity for a longer ride. Steffen had suggested a
route through the Chilterns, which promised lots of ups and downs, and brought
his vintage road bike. The weather did indeed not let us down and it was a
great day out with a rewarding if not exhausting ride of over 90km.
Unfortunately the longest downhill stretch was a very poor road littered with
potholes so we had to be very cautious and couldn’t nearly go full speed,
which was a slight downer.&lt;/p&gt;

&lt;p&gt;For our slightly-more-than-halfway stop in West Wycombe we were rewarded with
a great view from a hill that hosts the Church of St. Lawrence and the
Dashwood Mausoleum. The mausoleum is a very fascinating and peculiar
construction, which seems somewhat out of place.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/jpZoBsqVLX9JSqmE-YelswX0fp3JrAKwXB5-IENbkJDVIFJreBvt8vN6pftc66JzNAng80KDBFTeuhjlLXhYQz8zE1A8TRbNXXraCBSKqX2qI8cp0xDWkqu3PrQIYwvGL2rTOkjR27xCdCVGTaOYuEdkC8hdrEYKxdo3jXYUjfjNoFaxJjh57ZXJ-d_n7mmt9NdAoEM0920f15RAOmRgkHjbHUss3ZUoim-uU5mrcbiZMsez037OS_6uKFFiIoGWC-x7rnI-n8h9WSXwhZ_5Ldd_Jp5wUIS-id_8M5-pqgcrSx4CE84dsZW1mkiMUkf98eb8oNjs0TrUSNHsQMd30BYtF35a8YVy6Vmvw34gUJUFQofzFWCebEtg7lrBWLN3EY5hIivlkPplUPZ_04eyQ7fYqU9AG1kjlC8g7C2PD948z3fzLKiqNNrei9xE993yo-zbDZJFNoTS0L_fRt3sLcS0UsiWFliJwxnv9RLIRSfvxhkhdPwIiMWgeS304TcBVMKFPtsDrjgu-3jszps25CwsNxT_IWmQ8-nUb3SUYsDL=w720&quot; alt=&quot;&quot; /&gt;
&lt;em&gt;Church of St. Lawrence&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/BMq3y_YwBlJRqweH2Mqv4RePjFo0QktkGxb7n_lDsmrv7RCaab29S8YOf29Msw3PAkBmrFwyWi3z9_uzaPoQJcGwkPpYA1KZ97VxbumQmf029jO3TcftE09-NHLKJ_ViyIQgDCcLWucdBi8OFDKop5aWBMH80FpjvbWtCB8c_f8FQsHl75tXX6zMmFlAjgTnMaWBIKgFPe7F4-3Ol8RYiznJLeihXCRFWQkUI5N4Z-xIXg5kXvS7LDyu00GOoEAAZegro-Cj_zuOAcdFPmLioSsU-Mnf9eM1fN533AsS4mca7YmO4pvayvh54f6Sax7PuJFF97ERWSI_siEtukY9dPbCrcYx9jBebToT5S5og_UASffyX4Ci4Iyknf1Kax_QWjt38b56LymaNcoUxFUFQgbjY10qzVfCY8LZd0jMOyGn0ehB_vF5nDAQow635jnEmrlgJ3BCg6-FMk266x2zTt-jpPFvJhP_0_w113MiwcVm7cKu6JbQzv54hXjTOfKga6PnBcLMqXPrp4b0UQxEKl6QmJNIaFqGoxk6k3TsYvWm=w720&quot; alt=&quot;&quot; /&gt;
&lt;em&gt;Dashwood Mausoleum&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/7g0f2nPH7DIDKPzIQPHnDgdu-GNED0NbqxTiF2V1jq0ovywAlNu3K6ULDD4uC2lpRnj6Mo9SG0j7Kml2c4L4qxJO2TL_MaqMMiq7uEOXSePiWcwYgMi2gmbIO5DVg5beTxLANo4Yy98FWXvjIc7NvV-uDKtUcODdGbDYO-kK6l5DKt-94Nncup9udGivdsujL4djiefvmIZDHXHc5jHyPAQTC2K1tKxlra_lDrZIuwhgPZD-ttGMXb27KZwMe7rj2KnoxAfxmUdNR9fS2I3y5Ki2GTFQfPU_6BYDpZ-Pp3XL2I34bEmXOfE7DArnqOhO3a39WkgRJ10nOhN2tT2CEclIoLmkD9LHirDGsG2j_wtlLpGgbRdAx6Vz48uRWA12qavrltF7d67lnzkOA0dLy2od0VQCbU6T6HfdfOnkQm2UOMmpVIc9UTNpFOvfiYVdZhs5R_tkWXyFMdJV6E-1JR3tGJyIWaI-16YOfCmYPbSUgJ9kqbAqHW4ck9x-fStyWVekLaDA0n-cSR42dPZZ1cLwdy5Pxli0O8h1jCedN9P0=w720&quot; alt=&quot;&quot; /&gt;
&lt;em&gt;Dashwood Mausoleum&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We gave the “Hellfire Caves” a miss, given that their lurid name indicated it
was either a family affair or a tourist trap. The admission was rather pricey
and we only had sunglasses, which aren’t exactly helping in a cave…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/XhUX5Odr09z6eFnQ0KB8wtbJipcWuAtpNapgLgBXg_6erHMb2YWcnwLmN1InK5HPUNNV8pcRFtaCm4lYIxv0SUwJ0YwCIDQoN4sPjQ6Xmo4ItoqSY_HZWWfxz-JBtxHyqWshJND_-zT0S_DAVqMyd-371AnvJUGrdr61-zIvZX6_IBt8C88KqtJJkuGmiWnVTg80hljTcAVrQr0yxHM0P04fnpCM8AWl7xdvEuhUo95kag2NObpTqpy2z3ACBu0y3r69XC5ysdVHvI1zTwkgV2hnX5sQEdAgL8Fsit72NQQITmn9f5r6heFr8RvMbyv5Of_ak2hhYGbVfSCqBctCOwocAKKENU5rbENj0EthKS0jHUc0OQ8mABb9Ay9N64LQ6jEBIv3-QmzrTlAswE4PyyXzGNxxJiS2BiJfCIb3JvLgwIq1tAdpQEm7JpL0_YLqfT0b8kQc4EbRiHkkqhw2dm1c4UHt2pgakln9-CrFk7E0q9TQzmOUmCpwR8s5rRb2rw5UtFkTALnMenAumL8XnfC0ryfHCT6pgliaB-WlNyCA=w720&quot; alt=&quot;&quot; /&gt;
&lt;em&gt;Hellfire Caves&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Later we passed the rather picturesque riverside town of Marlow, which seems
like a prettier and quieter version of the somewhat overly hyped Henley.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/byy8H6L3z2QwK0po1HeCSXt-BdX8iaKWrGFvJAbGwIsR4UX9k6nsKBI0e4BLYynZS1EEPy0xbP4v-B17ieQ_Y45JAdV-1h1tNCI2H08ANbqY9_DFyS56ZYZlx06_vKF2dSvVvmnC0vK8xbalWeVch29NCFAlQ9Eni-BeDHnbd9yyPV_6FQ1daI0JIu4SGmCLX-sxGwfNpaDpXLCKU7iUNfI9X4Kpc3SyRNzylRPMtJcbRNLc0an4DWpVU8NSQ7zUyoisQFrMavlyUQHpbKsKn60lVkDQZZyPAwnf6KKM7tyfuBqqAoj34nLEhgrZpK-Kf5LMbzZMPC39_FkXmaM80T18UhFDjLp9phSQ_rnzJ5ueHeQyRpPYprV9xh0oqpcghPjaGqLY14MqaQd-NlYF4QNQbyxBFFpFXUzqmpoI3Hbwhq8BFZMXW-XZEqqjp_KOFxuhGpZ60eMKyDyF0JkdqDuO5OxQnmbv3pYIMc_8w6U-cqQ4pS09mRabQQfrdzGYePzHN6BzgCgHYl5JaSkCUt991pu1UPRZZqIEiK2jIYtn=w720&quot; alt=&quot;&quot; /&gt;
&lt;em&gt;All Saint’s Church Marlow&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/H0h65ijfIvT_DIbU24e9y_9w2Webp4uyiMwcTT4TnLx3d8KVvnB4U23aimmga53LVB6RvORdZKK-RI4bxUUL5iY4EgsALlwq2EsV-Ocjf1AW6ZlqCnwlzOCS83w-zHs9UgWK_-9kZiVOHPDD2zySNqnmY3MyMLHPDK1sIk7mgwOxwvkU36-YIgVB8IebF9tePUlLk7NkqEkR_35JWUF5IbOPklhgJ-1NT3BDcs1Y02yEUFtEDdSoz0VahswBl9JyJayi5cWWuio1SiFtYgawJEpCG7L1D3aYtO9r_uF3VyPu9SlfGP5PtQmUeW72aNAO5gIP5iN_VFfdOfqS2QSoSxQxPv_s96-PWgZHdTk2eUddd7RU36wIr2JXljRJ4Gak7NRgKn5spzbz43GKnMemXkzQP30mTyXEivRccHrdAw6RJpal-WG_LTQ82ZEZgz_qxYF_atfsY-4tS50j6eoTYesCqB0K9QQtKigS-Xz9gK6Wptkqu65e2m6xWmjPeZFGf1O4smLUhuSmsGaaSYUNtnx5_Zs-QIbfOtsKP4Mh1F-t=w720&quot; alt=&quot;&quot; /&gt;
&lt;em&gt;The Compleat Angler&lt;/em&gt;&lt;/p&gt;
</content>
   </entry>
   
 
   
   <entry>
     <title>How to speed up your vim startup time</title>
     <link href="https://kynan.github.io/blog/2015/07/31/how-to-speed-up-your-vim-startup-time"/>
     <updated>2015-07-31T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2015/07/31/how-to-speed-up-your-vim-startup-time</id>
     <content type="html">&lt;p&gt;My go-to editor for pretty much anything is vim. And like any serious vim user
I use numerous plugins to extend and improve the editing experience. Natually
there’s a cost to that, namely an increase in vim startup time. Compared to
almost any GUI application vim starts up almost instantaneously. Still I
noticed that slight delay and it had been bothering me for a while until I
finally got round to investigate and tweak.&lt;/p&gt;

&lt;p&gt;Thankfully vim makes profiling the startup time really convenient by providing
a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--startuptime&lt;/code&gt; flag to write timings for loading your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vimrc&lt;/code&gt; and plugins
to a file, which looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;times in msec
 clock   self+sourced   self:  sourced script
 clock   elapsed:              other lines

000.007  000.007: --- VIM STARTING ---
002.399  002.392: Allocated generic buffers
002.468  000.069: locale set
004.946  002.478: GUI prepared
004.954  000.008: clipboard setup
004.967  000.013: window checked
016.181  011.214: inits 1
016.187  000.006: parsing arguments
016.188  000.001: expanding arguments
016.212  000.024: shell init
017.230  001.018: Termcap init
018.203  000.973: inits 2
018.390  000.187: init highlight
022.090  002.745  002.745: sourcing /usr/share/vim/vim74/debian.vim
026.679  000.296  000.296: sourcing /usr/share/vim/vim74/syntax/syncolor.vim
026.805  001.004  000.708: sourcing /usr/share/vim/vim74/syntax/synload.vim
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The interesting measurements are those concerned with sourcing files, so focus
your attention on the 3rd column to see where time is spent. Short of trimming
down your vimrc (mine takes about 18ms to load by itself) and cutting down on
the number of plugins, the plugin manager can also make a difference. I used
&lt;a href=&quot;https://github.com/tpope/vim-pathogen&quot;&gt;pathogen&lt;/a&gt; for a long time, before switching to &lt;a href=&quot;https://github.com/gmarik/vundle&quot;&gt;Vundle&lt;/a&gt; (because it’s easier
to have your plugin manager handle Git repositories rather than having to
manually add them as submodules to your dotfile repository or similar). Then I
came across &lt;a href=&quot;https://github.com/junegunn/vim-plug&quot;&gt;vim-plug&lt;/a&gt; and was intrigued by its &lt;a href=&quot;https://github.com/junegunn/vim-plug#on-demand-loading-of-plugins&quot;&gt;on-demand loading
feature&lt;/a&gt;.
That allows loading plugins for specific file types or only on the first
invocation of a certain command. The latter is particularly useful for plugins
you don’t need to have active all the time. In my case this was particularly
useful for &lt;a href=&quot;https://github.com/scrooloose/nerdtree&quot;&gt;NERDtree&lt;/a&gt; and &lt;a href=&quot;https://github.com/kynan/dokuvimki&quot;&gt;DokuVimKi&lt;/a&gt;, which both take a significant time to
load. Looking carefully at the startup time output I also noticed I was
sourcing filetype plugins twice! The offending line in my vimrc was quickly
found, which shaved off some additional milliseconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Switching to &lt;a href=&quot;https://github.com/junegunn/vim-plug&quot;&gt;vim-plug&lt;/a&gt;, getting rid of some unnecessary plugins,
loading others only on demand and uncluttering my vimrc I managed to cut down
my vim startup by more than half, from close to 250ms to only about 120ms.&lt;/p&gt;

</content>
   </entry>
   
 
   
   <entry>
     <title>Plotting Christmas gift tags using a Raspberry Pi and resin.io</title>
     <link href="https://kynan.github.io/blog/2014/12/23/plotting-christmas-gift-tags-using-a-raspberry-pi"/>
     <updated>2014-12-23T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2014/12/23/plotting-christmas-gift-tags-using-a-raspberry-pi</id>
     <content type="html">&lt;p&gt;On the first weekend of December, the fine folks from &lt;a href=&quot;http://resin.io&quot;&gt;resin.io&lt;/a&gt; put on an
&lt;a href=&quot;http://fablablondon.org/events/xmas-iot-hackathon-with-resin-io/&quot;&gt;Xmas IoT Hackathon&lt;/a&gt; at the &lt;a href=&quot;http://fablablondon.org&quot;&gt;London Fab Lab&lt;/a&gt;. The Fab Lab is a maker space for
digital fabrication and rapid prototyping. They host classes and events (like
hackathons) and members can use a variety of tools for wood and metal working
as well as 3D printers and a laser cutter, which you can use e.g. to cut a
gingerbread house like this one (made from real gingerbread!):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/-jkHO2VE6F-A/VN9LWuFjZCI/AAAAAAAAzAs/teC6cQLf-4E/w636-h477-no/IMG_20141206_170241.jpg&quot; alt=&quot;Gingerbread house&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Appropriate to the occassion, the resin folks brought boxes full of hardware:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/-ftbgn7eVD6w/VN9LWsBisZI/AAAAAAAAzAs/Un0M0opMXso/w636-h477-no/IMG_20141206_114759.jpg&quot; alt=&quot;Hardware&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There was therefore no shortage of Raspberry Pis and all kinds of accessoires.
Teams were formed and a range of exciting hacks started taking shape. I teamed
up with &lt;a href=&quot;https://twitter.com/M6_D6&quot;&gt;Mark&lt;/a&gt;, a good friend and hackathon buddy, who had brought along a
Graphtec Silhouette Portrait cutter/plotter which we were intending to hack:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/-T2KvqyUBock/VN9LWo6hOmI/AAAAAAAAzAs/Qpz08KntBcQ/w636-h477-no/IMG_20141206_130622.jpg&quot; alt=&quot;Graphtec Silhouette Portrait&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You’d normally use this to cut out shapes previously created with their
proprietary software Silhouette Studio. Of course that’s only the intended
use. Replacing the knife by a silver sharpie turns the Silhouette into an
electronic circuit plotter - for very simple circuits. Here’s a
proof-of-concept circuit used to power an LCD with a button cell:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/-6e_iEbRmYyM/VN9LWkiLmKI/AAAAAAAAzAs/MHIT7syrkM4/w636-h848-no/IMG_20141206_133959.jpg&quot; alt=&quot;Circuit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Our plan was to the Silhouette into a Christmas gift tag plotter, powered by a
Raspberry Pi. Using Silhouette Studio to drive the plotter was of course no
option. We had to find a pure command line, open source solution to generate
the template and feed it to the plotter which also needed to run on the ARM
processor of the Pi. After some searching and experimenting we found a Python
based driver, which after &lt;a href=&quot;https://github.com/kynan/graphtecprint&quot;&gt;a little tweaking&lt;/a&gt; was able to digest a postscript
file and get it plotted. Here are some experimental results:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/-OJp-PtnPV_Y/VN9LWhxeCkI/AAAAAAAAzAs/o-yyfmbTGVM/w636-h477-no/IMG_20141206_200249.jpg&quot; alt=&quot;Experiments&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We generated the gift tags using &lt;a href=&quot;http://paperjs.org&quot;&gt;paper.js&lt;/a&gt;: The user puts in five names of
friends to create tags for and we place the names, framed each by two
automatically generated snowflakes, on a canvas and have &lt;a href=&quot;http://paperjs.org&quot;&gt;paper.js&lt;/a&gt; save the
canvas as SVG. Now there were only two pieces missing: one was converting from
svg to postscript, which &lt;a href=&quot;http://inkscape.org&quot;&gt;inkscape&lt;/a&gt; happily does for us, even on the command
line. The final piece was then a simple &lt;a href=&quot;http://nodejs.org&quot;&gt;node.js&lt;/a&gt; server to put everything
together: A form to put the names in, which when submitted generates an SVG
string, which is POSTed to the server, where it is saved to a file, converted
to postscript via inkscape and then fed to the plotter. Job done! Well,
almost, we still had to deploy it to the Pi.&lt;/p&gt;

&lt;p&gt;Fortunately &lt;a href=&quot;http://resin.io&quot;&gt;resin.io&lt;/a&gt;, a deployment service for IoT devices, makes that
rather simple: you create an application on the &lt;a href=&quot;http://resin.io&quot;&gt;resin.io&lt;/a&gt; dashboard, download
the base image for your device, flash it on the SD card, pop it into the Pi
and boot it. After a few minutes the device comes online at the dashboard.
When you then push to the Git repository associated with the project, your new
code is deployed on the Pi, &lt;a href=&quot;http://heroku.com&quot;&gt;heroku&lt;/a&gt;-style, but cooler (because of the &lt;a href=&quot;http://docs.resin.io/img/screenshots/git_pushed.png&quot;&gt;blue
unicorn&lt;/a&gt; and cause it run on your device!).&lt;/p&gt;

&lt;p&gt;The device is provisioned via &lt;a href=&quot;http://docker.com&quot;&gt;docker&lt;/a&gt; and &lt;a href=&quot;http://resin.io&quot;&gt;resin.io&lt;/a&gt; provide a base container
(based on &lt;a href=&quot;http://raspbian.org&quot;&gt;Raspbian&lt;/a&gt; wheezy) that comes with node.js preinstalled. Python, git
and inkscape are easily installed via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; package manager and we simply
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone&lt;/code&gt; the graphtec driver. The container automatically runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm
install&lt;/code&gt; and when successful &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm start&lt;/code&gt; to run our server. So far, so
straightforward, but there were two small issues to figure out: one was
installing the font we were using for the names and refreshing the font cache
and the other was detecting the printer, which required starting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;udevd&lt;/code&gt;
manually.  Having that sorted, our first test was finally sucessful:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/-evUBXakjqkA/VJIf2-ifOUI/AAAAAAAAvqE/js-4JPhAVOU/w636-h848-no/IMG_20141218_002904.jpg&quot; alt=&quot;Gift tags&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We had a really great weekend at the Fab Lab and even made the 2nd prize with
our hack. Thanks to the &lt;a href=&quot;http://resin.io&quot;&gt;resin.io&lt;/a&gt; folks for putting it on and hopefully until
the next one!&lt;/p&gt;

</content>
   </entry>
   
 
   
   <entry>
     <title>How to update expired repository keys in Debian / Ubuntu</title>
     <link href="https://kynan.github.io/blog/2014/07/24/how-to-update-expired-repository-keys-in-debian-ubuntu"/>
     <updated>2014-07-24T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2014/07/24/how-to-update-expired-repository-keys-in-debian-ubuntu</id>
     <content type="html">&lt;p&gt;When using third party package repositories, you occassionally might need to
update expired repository keys. An expired key leads to an error message
during a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get update&lt;/code&gt; similar to the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used.
GPG error: http://build.i3wm.org raring Release:
The following signatures were invalid: KEYEXPIRED 1396011159 KEYEXPIRED 1396011159 KEYEXPIRED 1396011159
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To find any expired repository keys and their IDs, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-key&lt;/code&gt; as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-key list | grep expired
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You will get a result similar to the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pub   4096R/BE1DB1F1 2011-03-29 [expired: 2014-03-28]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key ID is the bit after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BE1DB1F1&lt;/code&gt; in this case.&lt;/p&gt;

&lt;p&gt;To update the key, run&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-key adv --recv-keys --keyserver keys.gnupg.net BE1DB1F1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The repository will then be updated with the next &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get update&lt;/code&gt;.&lt;/p&gt;
</content>
   </entry>
   
 
   
   <entry>
     <title>Taarifa reporting for International Medical Corps UK at hack4good London</title>
     <link href="https://kynan.github.io/blog/2013/10/06/taarifa-reporting-for-international-medical-corps-uk-at-hack4good-london"/>
     <updated>2013-10-06T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2013/10/06/taarifa-reporting-for-international-medical-corps-uk-at-hack4good-london</id>
     <content type="html">&lt;p&gt;On the weekend of 4-6th October I was part of &lt;a href=&quot;http://hack4good.io&quot;&gt;#hack4good&lt;/a&gt; in London
working with &lt;a href=&quot;http://chrisadams.me.uk&quot;&gt;Chris Adams&lt;/a&gt;, &lt;a href=&quot;http://www.simplyunderstand.com&quot;&gt;Corinne Pritchard&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/nickpstanton&quot;&gt;Nick Stanton&lt;/a&gt;
tackling multi-agency reporting at refugee camps for &lt;a href=&quot;http://www.internationalmedicalcorps.org.uk&quot;&gt;International Medical
Corps UK&lt;/a&gt; with the &lt;a href=&quot;http://taarifa.org&quot;&gt;Taarifa&lt;/a&gt; platform.&lt;/p&gt;

&lt;p&gt;We approached the problem from two angles: One was developing a workflow for a
paper-based reporting system, where report forms are automatically generated
for a specific area like a refugee camp. Refugees would be able make a report
by filling in the form themselves with the help of a simple iconography to
overcome the language barrier, mark the location on a map and drop it into a
collection box. The forms are machine readable and can be digitized in an
automated process.&lt;/p&gt;

&lt;p&gt;The other was the technical challenge of providing a platform that could
process a variety of different reports that various agencies working at a
refugee camp need to deal with from santiation over general feedback to sexual
exploitation. These can be aggregated in one place by the &lt;a href=&quot;http://api.taarifa.org&quot;&gt;Taarifa API&lt;/a&gt;
which I’ve extended to be able to handle any report schema. Creating a new
report type is a simple as sending a POST request describing the schema to the
API in JSON format.  Reports can then be submitted immediately via the JSON
API or via an automatically generated web form. All of that is live and
operational at &lt;a href=&quot;http://api.taarifa.org&quot;&gt;http://api.taarifa.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There was quite a bit of media coverage around &lt;a href=&quot;http://hack4good.io&quot;&gt;#hack4good&lt;/a&gt;: &lt;a href=&quot;http://www.channel4.com/news/hackers-hack4good-charity-event-san-francisco-coding&quot;&gt;Channel
4&lt;/a&gt;, &lt;a href=&quot;http://uk.news.yahoo.com/video/hack-good-world-hackers-unite-090405127.html&quot;&gt;SkyNews&lt;/a&gt; and a &lt;a href=&quot;https://www.youtube.com/watch?v=znkKOeGStpQ&quot;&gt;video of the London event&lt;/a&gt; by &lt;a href=&quot;http://bigbang-lab.com&quot;&gt;Big Bang
Lab&lt;/a&gt;.&lt;/p&gt;

</content>
   </entry>
   
 
   
   <entry>
     <title>C pointers to multi-dimensional assumed shape Fortran arrays with gfortran</title>
     <link href="https://kynan.github.io/blog/2013/08/12/c-pointers-to-multi-dimensional-assumed-shape-fortran-arrays-with-gfortran"/>
     <updated>2013-08-12T00:00:00+00:00</updated>
     <id>https://kynan.github.io/blog/2013/08/12/c-pointers-to-multi-dimensional-assumed-shape-fortran-arrays-with-gfortran</id>
     <content type="html">&lt;p&gt;Interoperability between C and Fortran codes is often complicated by Fortran’s
support for “true” multi-dimensional arrays. In addition, Fortran 90 supports
assumed shape (also referred to as deferred shape) arrays, where the extent
sizes are determined at runtime. Actual arguments for array dummy arguments
can be array slices that may be of non-unit stride. In short, an assumed size
Fortran array can reference non-contiguous chunks of memory.&lt;/p&gt;

&lt;p&gt;Therefore, the compiler will reject an attempt to obtain a raw data pointer of
a Fortran array to pass to a C function like in the following module:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kynan/1e1b2ee82b2999b75044.js?file=test1.F90&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gfortran&lt;/code&gt; 4.7 emits the following error message:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Error: Assumed-shape array &apos;v&apos; at (1) cannot be an argument to the procedure &apos;c_loc&apos; because it is not C interoperable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What if you know that the assumed-shaped array passed to your function is
actually contiguous (or you choose to leave it the caller’s responsibility)?
Surely the compiler should be able to give you a pointer to the first array
element like so:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kynan/1e1b2ee82b2999b75044.js?file=test2.F90&quot;&gt; &lt;/script&gt;

&lt;p&gt;Turns out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gfortran&lt;/code&gt; doesn’t and generates the same error as before. The
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gfortran&lt;/code&gt; developers have recognized this as a &lt;a href=&quot;http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50269&quot;&gt;bug&lt;/a&gt;, which will be fixed in
the 4.9 release.&lt;/p&gt;

&lt;p&gt;In the meantime you can work around it by defining a pure function that simply
returns the first argument of an assumed shape array. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gfortran&lt;/code&gt; is happy with
that since now the type of the argument passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c_loc&lt;/code&gt; is a scalar and
therefore interoperable:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kynan/1e1b2ee82b2999b75044.js?file=test3.F90&quot;&gt; &lt;/script&gt;

&lt;p&gt;However, this workaround confuses Intel’s and PGI’s Fortran compilers, so
simply escape it using the a preprocessor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#define&lt;/code&gt;:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/kynan/1e1b2ee82b2999b75044.js?file=test4.F90&quot;&gt; &lt;/script&gt;

</content>
   </entry>
   
 

</feed>
