<?xml version="1.0" encoding="UTF-8"?>
<feed
  xmlns="http://www.w3.org/2005/Atom"
  xmlns:thr="http://purl.org/syndication/thread/1.0"
  xml:lang="en"
   >
  <title type="text">All Unkept</title>
  <subtitle type="text"></subtitle>

  <updated>2013-05-16T09:47:00Z</updated>
  <generator uri="http://blogofile.com/">Blogofile</generator>

  <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog" />
  <id>http://lukeplant.me.uk/blog/feed/atom/</id>
  <link rel="self" type="application/atom+xml" href="http://lukeplant.me.uk/blog/feed/atom/" />
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[Controlling Sozi remotely from an Android phone]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/controlling-sozi-remotely-from-an-android-phone" />
    <id>http://lukeplant.me.uk/blog/posts/controlling-sozi-remotely-from-an-android-phone</id>
    <updated>2013-03-11T15:37:57Z</updated>
    <published>2013-03-11T15:37:57Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="Python" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[Controlling Sozi remotely from an Android phone]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/controlling-sozi-remotely-from-an-android-phone"><![CDATA[<div class="document">
<p>I'm due to give a talk next Sunday evening, and I've created a presentation
using the very cool <a class="reference external" href="http://sozi.baierouge.fr/wiki/en:welcome">Sozi</a>. Sozi is
quite like <a class="reference external" href="http://prezi.com/">Prezi</a>, but open source and free, and its
output is a standard <a class="reference external" href="http://en.wikipedia.org/wiki/Scalable_Vector_Graphics">SVG</a> file (with a bit of
Javascript), so it can be displayed in several web browsers.</p>
<p>(On the other hand, for many people Sozi is probably a lot harder to use for
simple presentations than Prezi, as it works as a plugin to <a class="reference external" href="http://inkscape.org/">Inkscape</a>, a general purpose drawing program, but, to me, it is
simply unacceptable to depend on the continued existence of a company, and a
subscription fee, to be able to continue to view/edit your presentations).</p>
<p>One problem with not using ‘standard’ software like Powerpoint is that
proprietary tools like hand-held remote controls for Powerpoint won't work, or
won't work easily (and I don't want to find out that it won't work when I turn
up). But I've found a solution that works for me:</p>
<ol class="arabic">
<li><p class="first">On my Android phone, I installed <a class="reference external" href="http://www.coversal.com/">Coversal</a>,
which is an app designed for controlling media players, but allows custom
commands to be executed, so actually works fine for presentations.</p>
</li>
<li><p class="first">Within Coversal, I installed the ‘SSH Custom’ plugin.</p>
</li>
<li><p class="first">On my Linux laptop, I installed openssh-server to allow the app to log in to
my machine. To avoid putting my laptop password on the phone, I created an
SSH keypair for this app, transferred to private key to the phone, and added
the public key to ~/.ssh/authorized_keys.</p>
</li>
<li><p class="first">I created a script on my laptop that can control Sozi running in Chrome:</p>
<pre class="code python literal-block">
<span class="c">#!/usr/bin/env python</span>

<span class="c"># controlsozi</span>

<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">logging</span>

<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>

<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="s">'/tmp/controlsozi.log'</span><span class="p">,</span> <span class="n">filemode</span><span class="o">=</span><span class="s">&quot;a&quot;</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_window</span><span class="p">():</span>
    <span class="c"># Assumes that there is just a single Google Chrome window open, and this</span>
    <span class="c"># has the presentation in it.</span>
    <span class="n">windows</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">check_output</span><span class="p">(</span><span class="s">&quot;DISPLAY=:0 wmctrl -l -x | egrep 'google-chrome|gnome-www-browser'&quot;</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">windows</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">send_text</span><span class="p">(</span><span class="n">text</span><span class="p">):</span>
    <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s">&quot;DISPLAY=:0 xvkbd -window </span><span class="si">%s</span><span class="s"> -text '</span><span class="si">%s</span><span class="s">'&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">get_window</span><span class="p">(),</span> <span class="n">text</span><span class="p">))</span>

<span class="n">ACTIONS</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'previous'</span><span class="p">:</span>      <span class="s">&quot;\[Left]&quot;</span><span class="p">,</span>
    <span class="s">'next'</span><span class="p">:</span>          <span class="s">&quot;\[Right]&quot;</span><span class="p">,</span>
    <span class="s">'fullscreen'</span><span class="p">:</span>    <span class="s">&quot;\[F11]&quot;</span><span class="p">,</span>
    <span class="s">'previous_fast'</span><span class="p">:</span> <span class="s">&quot;\[Up]&quot;</span><span class="p">,</span>
    <span class="s">'next_fast'</span><span class="p">:</span>     <span class="s">&quot;\[Down]&quot;</span><span class="p">,</span>
    <span class="s">'start'</span><span class="p">:</span>         <span class="s">&quot;\[Home]&quot;</span><span class="p">,</span>
    <span class="s">'end'</span><span class="p">:</span>           <span class="s">&quot;\[End]&quot;</span><span class="p">,</span>
    <span class="s">'launch'</span><span class="p">:</span>        <span class="bp">None</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">def</span> <span class="nf">log_uncaught_exceptions</span><span class="p">(</span><span class="o">*</span><span class="n">exc_info</span><span class="p">):</span>
    <span class="n">logging</span><span class="o">.</span><span class="n">critical</span><span class="p">(</span><span class="s">'Unhandled exception:'</span><span class="p">,</span> <span class="n">exc_info</span><span class="o">=</span><span class="n">exc_info</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">excepthook</span> <span class="o">=</span> <span class="n">log_uncaught_exceptions</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&quot;Starting...&quot;</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">print</span> <span class="s">&quot;Usage: &quot;</span>
        <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">ACTIONS</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
            <span class="k">print</span> <span class="s">&quot;  &quot;</span> <span class="o">+</span> <span class="n">k</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">action</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
        <span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&quot;Action = </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="n">action</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">action</span> <span class="o">==</span> <span class="s">&quot;launch&quot;</span><span class="p">:</span>
            <span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s">&quot;DISPLAY=:0 nohup google-chrome '</span><span class="si">%s</span><span class="s">' &amp;&quot;</span> <span class="o">%</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">text</span> <span class="o">=</span> <span class="n">ACTIONS</span><span class="p">[</span><span class="n">action</span><span class="p">]</span>
            <span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&quot;Text = </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="n">text</span><span class="p">)</span>
            <span class="n">send_text</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>

    <span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&quot;Done&quot;</span><span class="p">)</span>
</pre>
<p>This relies on command-line tools wmctrl and xvkbd. With only small
modifications it should work for Firefox too. It is not 100% reliable, as it
works by sending keypresses to windows, but it works fine for me.</p>
</li>
<li><p class="first">I set up a profile in Coversal to connect to my laptop and use this script.</p>
<p>Coversal allows you to define the commands for certain actions — the list of
actions includes things like <tt class="docutils literal">up</tt>, <tt class="docutils literal">right</tt>, <tt class="docutils literal">next</tt> etc., but you can
add your own. You then assign actions to buttons, as desired.</p>
<p>So I simply I had to set commands like &quot;<tt class="docutils literal">/path/to/controlsozi next</tt>&quot;, and
then assign these actions to the buttons I wanted to use.</p>
<p>By setting the <tt class="docutils literal">start_playback</tt> command to &quot;<tt class="docutils literal">/path/to/controlsozi launch
'%s'</tt>&quot;, I could even use the file browser built in to Coversal to select the
presentation file to view, which is pretty neat.</p>
<p>The one thing I was really missing was physical buttons — with a touch
screen, it's so easy to hit the wrong place, and I wanted something to rest
my finger or thumb on.</p>
<p>I then realised that my phone does have a couple of physical buttons - the
volume control buttons on the side. By enabling the 'volume_up' and
'volume_down' buttons in Coversal and mapping them to the 'next' and
'previous' commands, the physical volume buttons get mapped to the same
actions, and they do the job just fine. I can use the rest of the controls on
the screen for the less common tasks.</p>
<img alt="/blogmedia/2013-03-14-coversal.png" src="/blogmedia/2013-03-14-coversal.png" />
</li>
</ol>
<p>For my phone to be able to connect to my laptop over SSH, there is one more
piece of configuration that must be done — the IP address of the laptop has to
be entered. If you have a local wifi, this is no problem — just connect the two
devices to the same wifi and they will be able to talk. My phone has a more
reliable option, though — it can itself operate as a wifi hotspot, creating a
wifi network that the laptop can connect to.</p>
<p>Conclusions:</p>
<ol class="arabic simple">
<li>Linux is awesome.</li>
<li>Smart phones are awesome.</li>
<li>Free Android apps are awesome (and I think it is the Linux culture which
encourages this).</li>
<li>Python is awesome.</li>
<li>Being a programmer and being able to plug all this stuff together is awesome.</li>
</ol>
</div>
]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[KTimeTracker replacement - TimeCult]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/ktimetracker-replacement-timecult/" />
    <id>http://lukeplant.me.uk/blog/posts/ktimetracker-replacement-timecult/</id>
    <updated>2012-02-24T17:57:32Z</updated>
    <published>2012-02-24T17:57:32Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="Python" />
    <category scheme="http://lukeplant.me.uk/blog" term="KDE" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[KTimeTracker replacement - TimeCult]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/ktimetracker-replacement-timecult/"><![CDATA[<div class="document">
<p>For a long time I haven't been able to find a decent replacement or alternative to KTimeTracker. But I've now succeeded: <a class="reference external" href="http://timecult.wordpress.com/">TimeCult</a>.</p>
<p>It is <a class="reference external" href="https://github.com/dyadix/TimeCult/issues/1">currently lacking an installer for Linux</a>, but it can be <a class="reference external" href="http://xmemory.tompium.com/2011/10/get-timecult-running-on-linux.html">made to work with Linux easily</a>.</p>
<p>I also found that it uses a very easy-to-deciper XML format, and, with the help of <a class="reference external" href="http://pypi.python.org/pypi/vobject">vobject</a>, the KTimeTracker ical files are only slightly harder to figure out, so I created a Python <a class="reference external" href="https://gist.github.com/1901236">script</a> that will do the conversion.</p>
<p>Use the script like this:</p>
<pre class="literal-block">
./ics_to_timecult.py ~/.kde/share/apps/ktimetracker/ktimetracker.ics &gt; out.tmt
</pre>
<p>It's here for reference - dependencies are vobject and elementtree:</p>
<pre class="code python literal-block">
<span class="c">#!/usr/bin/env python</span>
<span class="c"># Quick and dirty script to convert ktimetracker.ics files</span>
<span class="c"># into TimeCult files.</span>

<span class="kn">import</span> <span class="nn">os.path</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">uuid</span>

<span class="kn">import</span> <span class="nn">elementtree.ElementTree</span> <span class="kn">as</span> <span class="nn">ET</span>
<span class="kn">import</span> <span class="nn">vobject</span>


<span class="c"># Structures to store the tree of data. Use native Python data types, and</span>
<span class="c"># convert to what TimeCult expects when we 'render'</span>

<span class="k">class</span> <span class="nc">Struct</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">__dict__</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">kwargs</span><span class="p">)</span>


<span class="k">class</span> <span class="nc">TimeCult</span><span class="p">(</span><span class="n">Struct</span><span class="p">):</span>
    <span class="c"># &lt;timecult&gt;</span>
    <span class="c"># Eventual attributes:</span>
    <span class="c">#  name</span>
    <span class="c">#  projectTree</span>
    <span class="c">#  timeLog</span>

    <span class="k">def</span> <span class="nf">toXML</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">root</span> <span class="o">=</span> <span class="n">ET</span><span class="o">.</span><span class="n">XML</span><span class="p">(</span><span class="s">'&lt;timecult appVersion=&quot;0.12&quot; fileVersion=&quot;10&quot; /&gt;'</span><span class="p">)</span>
        <span class="n">root</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'uuid'</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">uuid</span><span class="o">.</span><span class="n">uuid1</span><span class="p">())</span>
        <span class="n">root</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>
        <span class="n">pt</span> <span class="o">=</span> <span class="n">ET</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s">'projectTree'</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">projectTree</span><span class="p">:</span>
            <span class="n">pt</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">n</span><span class="o">.</span><span class="n">toXML</span><span class="p">())</span>
        <span class="n">root</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">pt</span><span class="p">)</span>
        <span class="n">tl</span> <span class="o">=</span> <span class="n">ET</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s">'timeLog'</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">tr</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">timeLog</span><span class="p">:</span>
            <span class="n">tl</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">tr</span><span class="o">.</span><span class="n">toXML</span><span class="p">())</span>
        <span class="n">root</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">tl</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">root</span>

<span class="k">class</span> <span class="nc">Node</span><span class="p">(</span><span class="n">Struct</span><span class="p">):</span>
    <span class="c"># &lt;project&gt; and &lt;task&gt;</span>
    <span class="c"># Eventually will have these attributes:</span>
    <span class="c">#  vtodo</span>
    <span class="c">#  summary</span>
    <span class="c">#  uid</span>
    <span class="c">#  created</span>
    <span class="c">#  parent</span>
    <span class="c">#  children</span>
    <span class="c">#  type = &quot;project|task&quot;</span>
    <span class="c">#  finished</span>
    <span class="k">def</span> <span class="nf">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&quot;&lt;Node: uid=</span><span class="si">%s</span><span class="s"> summary=</span><span class="si">%r</span><span class="s">, parent=</span><span class="si">%r</span><span class="s">&gt;&quot;</span> <span class="o">%</span> <span class="p">(</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">uid</span><span class="p">,</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">summary</span><span class="p">,</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">summary</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">parent</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">else</span> <span class="bp">None</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">toXML</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">e</span> <span class="o">=</span> <span class="n">ET</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">type</span><span class="p">)</span>
        <span class="n">e</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'id'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">id</span>
        <span class="n">e</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">summary</span>
        <span class="n">e</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'created'</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">mktime</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">created</span><span class="o">.</span><span class="n">timetuple</span><span class="p">())</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">))</span>
        <span class="k">if</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="s">&quot;task&quot;</span><span class="p">):</span>
            <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
            <span class="n">e</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'status'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'finished'</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">finished</span> <span class="k">else</span> <span class="s">'inProgress'</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">children</span><span class="p">:</span>
                <span class="n">e</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">toXML</span><span class="p">())</span>
        <span class="k">return</span> <span class="n">e</span>

<span class="k">class</span> <span class="nc">TimeRec</span><span class="p">(</span><span class="n">Struct</span><span class="p">):</span>
    <span class="c"># &lt;timerec&gt;</span>
    <span class="c"># Eventual attributes:</span>
    <span class="c">#  startTime  # datetime</span>
    <span class="c">#  duration   # seconds</span>
    <span class="c">#  task       # Node</span>
    <span class="k">def</span> <span class="nf">toXML</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">e</span> <span class="o">=</span> <span class="n">ET</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s">&quot;timeRec&quot;</span><span class="p">)</span>
        <span class="n">e</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'taskId'</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">taskId</span>
        <span class="n">e</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'startTime'</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">mktime</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">startTime</span><span class="o">.</span><span class="n">timetuple</span><span class="p">())</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">))</span>
        <span class="n">e</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'duration'</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">duration</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">))</span>
        <span class="n">e</span><span class="o">.</span><span class="n">attrib</span><span class="p">[</span><span class="s">'notes'</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;&quot;</span>
        <span class="k">return</span> <span class="n">e</span>

<span class="k">def</span> <span class="nf">convert</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
    <span class="n">f</span> <span class="o">=</span> <span class="n">vobject</span><span class="o">.</span><span class="n">readOne</span><span class="p">(</span><span class="nb">file</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>

    <span class="c"># First parse Todos</span>
    <span class="n">nodes</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">vtodo</span> <span class="ow">in</span> <span class="n">f</span><span class="o">.</span><span class="n">vtodo_list</span><span class="p">:</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">Node</span><span class="p">(</span><span class="n">vtodo</span> <span class="o">=</span> <span class="n">vtodo</span><span class="p">,</span>
                 <span class="n">uid</span><span class="o">=</span><span class="n">vtodo</span><span class="o">.</span><span class="n">uid</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
                 <span class="n">summary</span><span class="o">=</span><span class="n">vtodo</span><span class="o">.</span><span class="n">summary</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
                 <span class="n">created</span><span class="o">=</span><span class="n">vtodo</span><span class="o">.</span><span class="n">created</span><span class="o">.</span><span class="n">value</span><span class="p">,</span>
                 <span class="n">finished</span><span class="o">=</span><span class="n">vtodo</span><span class="o">.</span><span class="n">contents</span><span class="p">[</span><span class="s">'percent-complete'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="s">&quot;100&quot;</span>
                 <span class="p">)</span>
        <span class="n">nodes</span><span class="p">[</span><span class="n">n</span><span class="o">.</span><span class="n">uid</span><span class="p">]</span> <span class="o">=</span> <span class="n">n</span>

    <span class="n">children</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">uid</span><span class="p">,</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">nodes</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
        <span class="n">vtodo</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="n">vtodo</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">related_uid</span> <span class="o">=</span> <span class="n">vtodo</span><span class="o">.</span><span class="n">contents</span><span class="p">[</span><span class="s">'related-to'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">value</span>
            <span class="n">node</span><span class="o">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">nodes</span><span class="p">[</span><span class="n">related_uid</span><span class="p">]</span>
            <span class="n">children</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">related_uid</span><span class="p">,</span> <span class="p">[])</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
        <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
            <span class="n">node</span><span class="o">.</span><span class="n">parent</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="k">for</span> <span class="n">uid</span><span class="p">,</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">nodes</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
        <span class="n">node</span><span class="o">.</span><span class="n">children</span> <span class="o">=</span> <span class="n">children</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">uid</span><span class="p">,</span> <span class="p">[])</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">children</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">node</span><span class="o">.</span><span class="n">type</span> <span class="o">=</span> <span class="s">&quot;task&quot;</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">node</span><span class="o">.</span><span class="n">type</span> <span class="o">=</span> <span class="s">&quot;project&quot;</span>



    <span class="n">autouid</span> <span class="o">=</span> <span class="mi">0</span> <span class="c"># For newly created Tasks</span>

    <span class="c"># Now find the time information in the ICS file</span>
    <span class="n">time_log</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">vevent</span> <span class="ow">in</span> <span class="n">f</span><span class="o">.</span><span class="n">vevent_list</span><span class="p">:</span>
        <span class="n">related_uid</span> <span class="o">=</span> <span class="n">vevent</span><span class="o">.</span><span class="n">contents</span><span class="p">[</span><span class="s">'related-to'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">value</span>
        <span class="n">task</span> <span class="o">=</span> <span class="n">nodes</span><span class="p">[</span><span class="n">related_uid</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">task</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="s">&quot;project&quot;</span><span class="p">:</span>
            <span class="c"># TimeCult can only cope with time events being against 'leaf' nodes</span>
            <span class="c"># in the tree. So we need to adjust and create an additional leaf</span>
            <span class="n">project</span> <span class="o">=</span> <span class="n">task</span>
            <span class="n">undefined_l</span> <span class="o">=</span> <span class="p">[</span><span class="n">t</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">project</span><span class="o">.</span><span class="n">children</span> <span class="k">if</span> <span class="n">t</span><span class="o">.</span><span class="n">uid</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">'autouid'</span><span class="p">)]</span>
            <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">undefined_l</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
                <span class="n">task</span> <span class="o">=</span> <span class="n">undefined_l</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">task</span> <span class="o">=</span> <span class="n">Node</span><span class="p">(</span><span class="n">uid</span><span class="o">=</span><span class="s">'autouid-</span><span class="si">%d</span><span class="s">'</span> <span class="o">%</span> <span class="n">autouid</span><span class="p">,</span>
                            <span class="n">summary</span><span class="o">=</span><span class="s">'other'</span><span class="p">,</span>
                            <span class="n">parent</span><span class="o">=</span><span class="n">project</span><span class="p">,</span>
                            <span class="nb">type</span><span class="o">=</span><span class="s">&quot;task&quot;</span><span class="p">,</span>
                            <span class="n">created</span><span class="o">=</span><span class="n">project</span><span class="o">.</span><span class="n">created</span><span class="p">,</span>
                            <span class="n">finished</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
                            <span class="n">children</span><span class="o">=</span><span class="p">[])</span>
                <span class="n">autouid</span> <span class="o">+=</span> <span class="mi">1</span>
                <span class="n">nodes</span><span class="p">[</span><span class="n">task</span><span class="o">.</span><span class="n">uid</span><span class="p">]</span> <span class="o">=</span> <span class="n">task</span>
                <span class="n">project</span><span class="o">.</span><span class="n">children</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">task</span><span class="p">)</span>

        <span class="n">dtstart</span> <span class="o">=</span> <span class="n">vevent</span><span class="o">.</span><span class="n">dtstart</span><span class="o">.</span><span class="n">value</span>
        <span class="n">dtend</span> <span class="o">=</span> <span class="n">vevent</span><span class="o">.</span><span class="n">dtend</span><span class="o">.</span><span class="n">value</span>
        <span class="k">if</span> <span class="n">dtstart</span><span class="o">.</span><span class="n">tzinfo</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">dtstart</span> <span class="o">=</span> <span class="n">dtstart</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">tzinfo</span><span class="o">=</span><span class="n">dtend</span><span class="o">.</span><span class="n">tzinfo</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">dtend</span><span class="o">.</span><span class="n">tzinfo</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">dtend</span> <span class="o">=</span> <span class="n">dtend</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">tzinfo</span><span class="o">=</span><span class="n">dtstart</span><span class="o">.</span><span class="n">tzinfo</span><span class="p">)</span>
        <span class="n">time_rec</span> <span class="o">=</span> <span class="n">TimeRec</span><span class="p">(</span><span class="n">startTime</span><span class="o">=</span><span class="n">dtstart</span><span class="p">,</span>
                           <span class="n">duration</span><span class="o">=</span><span class="p">(</span><span class="n">dtend</span> <span class="o">-</span> <span class="n">dtstart</span><span class="p">)</span><span class="o">.</span><span class="n">total_seconds</span><span class="p">(),</span>
                           <span class="n">task</span><span class="o">=</span><span class="n">task</span><span class="p">)</span>
        <span class="n">time_log</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">time_rec</span><span class="p">)</span>

    <span class="c"># Now need to assign IDs</span>
    <span class="n">taskId</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">u</span><span class="p">,</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">nodes</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
        <span class="n">taskId</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="n">n</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">taskId</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">tr</span> <span class="ow">in</span> <span class="n">time_log</span><span class="p">:</span>
        <span class="n">tr</span><span class="o">.</span><span class="n">taskId</span> <span class="o">=</span> <span class="n">tr</span><span class="o">.</span><span class="n">task</span><span class="o">.</span><span class="n">id</span>

    <span class="k">return</span> <span class="n">TimeCult</span><span class="p">(</span>
        <span class="n">name</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">filename</span><span class="p">),</span>
        <span class="n">projectTree</span><span class="o">=</span><span class="p">[</span><span class="n">n</span> <span class="k">for</span> <span class="n">u</span><span class="p">,</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">nodes</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="k">if</span> <span class="n">n</span><span class="o">.</span><span class="n">parent</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">],</span>
        <span class="n">timeLog</span><span class="o">=</span><span class="n">time_log</span><span class="p">)</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="n">filename</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">timecult</span> <span class="o">=</span> <span class="n">convert</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
    <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span>
        <span class="sd">&quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&quot;&quot;&quot;</span> <span class="o">+</span>
        <span class="n">ET</span><span class="o">.</span><span class="n">tostring</span><span class="p">(</span><span class="n">timecult</span><span class="o">.</span><span class="n">toXML</span><span class="p">()))</span>
</pre>
</div>
]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[My Bash prompt]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/my-bash-prompt/" />
    <id>http://lukeplant.me.uk/blog/posts/my-bash-prompt/</id>
    <updated>2011-02-28T23:32:42Z</updated>
    <published>2011-02-28T23:32:42Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="Software development" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[My Bash prompt]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/my-bash-prompt/"><![CDATA[<div class="document">
<p>I like my <a class="reference external" href="http://tldp.org/HOWTO/Bash-Prompt-HOWTO/">bash prompt</a>:</p>
<ol class="arabic simple">
<li>to include the current <a class="reference external" href="http://mercurial.selenic.com/">Mercurial</a>/<a class="reference external" href="http://git-scm.com/">Git</a> branch if applicable — otherwise I would quickly get in big trouble with committing things to the wrong branch in <a class="reference external" href="www.djangoproject.com">Django</a> etc.</li>
<li>to show the <strong>full</strong> current working directory - for similar reasons to above, as I often end up lost in a maze of twisty little directories, all alike.</li>
<li>to be always instantaneous (tricky, when 'hg branch' can take up to a second on a bad day due to startup time)</li>
<li>to work well with virtualenv (which adds the environment name to the beginning of the prompt)</li>
<li>to show the time (so that if I have a long running job that I didn't realise was going to be long running, all the info is there to find out how long it took).</li>
<li>to do all the above without confusing me!</li>
</ol>
<p>All of these together mean I need two lines, but this has advantages - it means the commands I type always start at the same column. I achieve (1) by a bash function that looks for .hg or .git recursively, and looks for '.hg/branch' where applicable. I achieve (6) by using some colours distinguish the different bits.</p>
<p>The end product looks like this - first without a virtualenv, then with:</p>
<img alt="/blogmedia/custom_bash_prompt.png" src="/blogmedia/custom_bash_prompt.png" />
<p>The code looks like this (in ~/.bashrc)</p>
<pre class="code bash literal-block">
<span class="c"># Prints &quot; $branchname&quot; if in a hg or git repo, otherwise nothing.
</span>print_branch_name<span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">[</span> -z <span class="s2">&quot;$1&quot;</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nv">curdir</span><span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>
    <span class="k">else
        </span><span class="nv">curdir</span><span class="o">=</span><span class="nv">$1</span>
    <span class="k">fi
    if</span> <span class="o">[</span> -d <span class="s2">&quot;$curdir/.hg&quot;</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nb">echo</span> -n <span class="s2">&quot; &quot;</span>
        <span class="k">if</span> <span class="o">[</span> -f  <span class="s2">&quot;$curdir/.hg/branch&quot;</span> <span class="o">]</span>
        <span class="k">then
            </span>cat <span class="s2">&quot;$curdir/.hg/branch&quot;</span>
        <span class="k">else
            </span><span class="nb">echo</span> <span class="s2">&quot;default&quot;</span>
        <span class="k">fi
        return </span>0
    <span class="k">elif</span> <span class="o">[</span> -d <span class="s2">&quot;$curdir/.git&quot;</span> <span class="o">]</span>
    <span class="k">then
        </span><span class="nb">echo</span> -n <span class="s2">&quot; &quot;</span>
        git branch --no-color 2&gt; /dev/null | sed -e <span class="s1">'/^[^*]/d'</span> -e <span class="s1">'s/* \(.*\)/\1/'</span>
    <span class="k">fi</span>
    <span class="c"># Recurse upwards
</span>    <span class="k">if</span> <span class="o">[</span> <span class="s2">&quot;$curdir&quot;</span> <span class="o">==</span> <span class="s1">'/'</span> <span class="o">]</span>
    <span class="k">then
        return </span>1
    <span class="k">else
        </span>print_branch_name <span class="sb">`</span>dirname <span class="s2">&quot;$curdir&quot;</span><span class="sb">`</span>
    <span class="k">fi</span>
<span class="o">}</span>

<span class="nv">e</span><span class="o">=</span><span class="se">\\\0</span>33
<span class="nb">export </span><span class="nv">PS1</span><span class="o">=</span><span class="s2">&quot;\[$e[1;36m\][\u&#64;\h \t]\[$e[1;33m\]\$(print_branch_name) \[$e[0m\]\w\n\[$e[1;37m\]——&gt; \[$e[0m\]&quot;</span>
</pre>
</div>
]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[player_do — control multiple media players from a single (command line) interface]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/player-do-control-multiple-media-players-from-a-single-command-line-interface/" />
    <id>http://lukeplant.me.uk/blog/posts/player-do-control-multiple-media-players-from-a-single-command-line-interface/</id>
    <updated>2010-07-15T16:23:05Z</updated>
    <published>2010-07-15T16:23:05Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="Python" />
    <category scheme="http://lukeplant.me.uk/blog" term="Music" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[player_do — control multiple media players from a single (command line) interface]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/player-do-control-multiple-media-players-from-a-single-command-line-interface/"><![CDATA[<div class="document">
<p>I tend to use several music/media players, such as <a class="reference external" href="http://nex.scrapping.cc/shell-fm/">shell-fm</a>, <a class="reference external" href="http://moc.daper.net/">moc</a> and <a class="reference external" href="http://code.google.com/p/clementine-player/">Clementine</a>.  And I often try out new ones.  In order to be able to use the media buttons on my keyboard for pausing/skipping, I wrote this <a class="reference external" href="http://pypi.python.org/pypi/playerdo">player_do Python script</a> to automatically route the commands to the right one.</p>
<p>Quick start:</p>
<ul>
<li><p class="first"><tt class="docutils literal">sudo easy_install playerdo</tt></p>
<p>Or, if you prefer this way like me, and have <tt class="docutils literal"><span class="pre">$HOME/.local/bin</span></tt> on your <tt class="docutils literal">$PATH</tt>:</p>
<p><tt class="docutils literal">easy_install <span class="pre">--prefix=$HOME/.local</span> playerdo</tt></p>
</li>
<li><p class="first"><tt class="docutils literal">player_do help</tt></p>
<p>Then use your window manager/desktop to setup keyboard shortcuts to <tt class="docutils literal">player_do playpause</tt>, <tt class="docutils literal">player_do next</tt> etc.</p>
</li>
</ul>
<p><a class="reference external" href="http://incise.org/mpris-remote.html">mpris-remote</a> does a similar job, but only for <a class="reference external" href="http://en.wikipedia.org/wiki/Media_Player_Remote_Interfacing_Specification">MPRIS players</a>, and I needed others.  My script also has an MPRIS backend, so it will support XMMS2, Amarok, Clementine, Exaile, Dragon Player and various others out of the box.  It also has support for shell-fm, moc, and rhythmbox.</p>
<p>If you have more than one running at a time, it tries to be intelligent about which one to talk to, although of course it is not always possible to read your mind correctly!</p>
<p>For other players that don't support MPRIS it should be quite easy to add a backend — please fork the <a class="reference external" href="http://bitbucket.org/spookylukey/playerdo">bitbucket repository</a>, have a look at the existing backends and send me your patches!</p>
</div>
]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[Leaving KDE]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/leaving-kde/" />
    <id>http://lukeplant.me.uk/blog/posts/leaving-kde/</id>
    <updated>2010-06-17T01:57:39Z</updated>
    <published>2010-06-17T01:57:39Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="KDE" />
    <category scheme="http://lukeplant.me.uk/blog" term="Rants" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[Leaving KDE]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/leaving-kde/"><![CDATA[<div class="document">
<p>After being a loyal fan of KDE for a long time, I've just switched to GNOME.</p>
<p>I'm afraid to say that since the KDE 4 series, I've been unable to recommend
KDE to anyone.  With KDE 4.0 to 4.3, that was due to general crashiness,
brokenness and bugs that are at the level of <em>embarrassing</em> — when a bug is
obvious enough and significant enough that a new user would be shocked to find it and I would have a hard time explaining how I could use software that is clearly inferior to whatever they were using before (usually Windows).</p>
<p>With KDE 4.4, most desktop instability has been ironed out, but it has been
replaced by problems that mean I can no longer stomach or risk staying with
KDE for myself, let alone recommend it to anyone else.  The final straw was
that the framework for storing PIM data (Akondai) is turning out no better
than <a class="reference external" href="http://lukeplant.me.uk/blog/posts/a-plea-to-kde-developers/">I feared</a> and is
‘losing’ some of my data.</p>
<p>I found that <a class="reference external" href="https://bugs.kde.org/show_bug.cgi?id=232773">some things added to the address book ‘never’
actually end up in the file they are supposed to be stored in</a>.  I've added an e-mail
address for a contact, and it appears to have added fine in KAddressBook.
The data persists even across reboots etc., so it isn't ‘lost’ in one sense.
Usually, the data appears in my std.vcf after a few seconds.  In this case,
however, four days later it still hasn't appeared in std.vcf.  That counts
as close enough to ‘never’ in my book, so it is fair to say it is ‘lost’.
This has happened intermittently since KDE 4.4.1.</p>
<p>One additional result of this is that KMail never sees that address — at the
moment it seems that it looks only in std.vcf, not Akonadi (and this is
probably the only reason I found the bug in the first place).  That's a
pain, but the more serious problem is that my data is just not where it is
supposed to be.  If I need to backup and restore, I imagine it would be
possible for that to succeed, if the Akonadi data was backed-up and restored
along with the underlying data.  But if you get that bit wrong, I can only
imagine a world of pain and subtle data corruption.  It is completely
unacceptable that my data is being stored in “my data files + completely
opaque Akonadi database” rather than just “my data files”.</p>
<p>Another bug is that <a class="reference external" href="https://bugs.kde.org/show_bug.cgi?id=232774">the Google contacts Akonadi plugin also loses data in a
serious way</a>, making it far
worse than useless.  The nature of this bug makes me think that either there
is a significant bug in the Akonadi framework, or that the framework API
made it is easy to get this wrong — both of which are worrying.</p>
<p>These bugs are on top my general concerns with the framework — it has always
been painful to work with, and the fact that it is even <strong>visible</strong> is a
major failing — the software layer it replaced was, after all, completely
invisible, just as it should have been.</p>
<p>From what I can tell, migration of KMail to Akonadi is going ahead at full
pace and will be mandatory soon.  My contact data is modest (500 Kb), so I
have been able to cope with moving it around and fixing some problems,
including some data loss and mistakes caused, I think, by the above bugs,
since backups are quick.  But my e-mail data is 2.5 Gb, and detecting
problems or errors is going to be massively harder.  I'm not willing to risk
my e-mail data with Akonadi, so I need a new e-mail client.</p>
<p>It turns out that Evolution works for me.  It stores data in mbox files,
which isn't my preferred (1 file per e-mail, like maildir or MH, is nicer
for various purposes), but it is a standard format, and since my backup tool
is 'diff' based it will still cope efficiently with it.  It even integrates
with Google contacts out of the box, and the calendar app integrates with
Google calendar, while allowing me to work offline.  This is what Akonadi is
supposed to offer one day, and I wish them the best of luck with that.  But
for now I'm going to stick with a system that prioritises the safety of my
data, and that I know works.  Evolution has a few significant niggles, but I
think I can live with them.</p>
<p>That leaves me with the choice of desktop.  Using programs from one desktop
suite generally is faster and easier and looks nicer.  I had already dropped
Konqueror for Chrome (Konqueror has fallen far too far behind these days),
and GNOME equivalents to most KDE apps work well enough — like
Empathy/Pidgin.  That tips the balance over to GNOME — and I can still use
apps like k3b for specialist work when I need them.</p>
<p>Most of my reasons for leaving GNOME in the first place (about 7 years ago I
think) have been resolved.  GNOME apps like Nautilus and Evolution are no
longer as excuciatingly slow and bloated as they used to be (and I don't
think it is just that my computer got faster), and actually feel quite
snappy most of the time.  In some cases they definitely outperform the
equivalent KDE default app (e.g. Eye of GNOME is very fast and works very
nicely, doing 90% of what I used Gwenview for before, but much faster).
Generally GNOME is much snappier as a desktop — it takes only about 5
seconds to start after I've logged in, which is <em>much</em> faster than KDE.</p>
<p>So I'm using Ubuntu's GNOME desktop, and I no longer have plasma and all the
plasmoids.  To be honest, I don't think I'm missing them.  I have all the
panel applets I need, and they are well designed and work properly.
Proliferation of plasmoids is like the proliferation of apps on Apple app
store — most of them are not worth having, and having to search through to
find the ones that are is an anti-feature (not to mention that the new
horizontal applet picker in KDE 4.4, which replaced the vertical one in KDE
4.3, is just awful — I don't understand how such a major regression happened).</p>
<p>I certainly won't shed a tear over Nepomuk/Strigi.  After <strong>weeks</strong> of it constant disk activity as they attempted to index my files, they never seemed to do anything useful. They have been replaced by Tracker, which works very well, and very quickly, and without being a huge resource hog. It does not update immediately when I add new files, but it is fast enough to know that when I really want to find something in a real situation, it is likely to be in the index already.  And it isn't the massive resource hog that Nepomuk was.</p>
<p>First experience with Ubuntu's GNOME has thrown up some problems.  Here are some niggles I've been able to fix/solve:</p>
<ul class="simple">
<li>PulseAudio - this breaks lots of things, especially games, and provides no
added benefit.  But <a class="reference external" href="http://art.ubuntuforums.org/showpost.php?p=9297829&amp;postcount=146">it can be removed</a>.
Perhaps the Ubuntu guys will remove it in future, or make it easier to
remove.  Lots of people and programs seem to have problems with it, it's
very difficult to see what value it is adding for most people.</li>
<li>To turn off desktop effects quickly (for better performance games)
- either: install fusion-icon (use &quot;fusion-icon -n&quot; as the command)
- or, to do it automatically for fullscreen games, set this option to true: /apps/compiz/general/screen0/options/unredirect_fullscreen_windows</li>
<li>To turn off screen locking on resume - there is setting is in gconf,
search for 'lock'.</li>
</ul>
<p>And here are some that are still bothering me a bit:</p>
<ul class="simple">
<li>Reading new e-mails with keyboard only - it's a bit buggy/confusing with Evolution.</li>
<li>Some options are hidden away in gconf, so it's difficult to know if it is
possible to change things.</li>
<li>GNOME has less configurability in general, or harder to get to it,
especially for creating new keyboard shortcuts.</li>
<li>Compiz doesn't always seem to obey or understand all metacity options
(e.g.  shortcut for fullscreen-ing windows), so it sometimes confusing
working out what is going on.</li>
<li>The GNOME file dialog boxes have improved a little bit since I was last using GNOME, but haven't begun to close the gap on KDE's far superior ones.</li>
</ul>
<p>But there are some niggles which are no longer there due to the switch from
KDE.  Overall, things are similar at this level.</p>
<p>To conclude, I think the KDE developers were far too ambitious with the KDE 4 series.  The cost has been that the only supported versions of KDE are ones that you cannot recommend to anyone, and that is a critical mistake in a competitive environment.  Meanwhile, GNOME has massively improved in speed and has incrementally added features that people actually need, instead of creating large frameworks that may or may not be robust and useful one day.</p>
</div>
]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[Linux and calendars - bad combination...]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/linux-and-calendars-bad-combination/" />
    <id>http://lukeplant.me.uk/blog/posts/linux-and-calendars-bad-combination/</id>
    <updated>2010-03-23T13:22:49Z</updated>
    <published>2010-03-23T13:22:49Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="KDE" />
    <category scheme="http://lukeplant.me.uk/blog" term="Rants" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[Linux and calendars - bad combination...]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/linux-and-calendars-bad-combination/"><![CDATA[<div class="document">
<p>So, I'd like to be able to access my sync the calendar on my computer with other devices, like my BlackBerry Curve.  This is 2010 right?</p>
<ul class="simple">
<li>I already blogged the <a class="reference external" href="http://lukeplant.me.uk/blog/posts/blackberry-curve-kde-pim-sync-on-kubuntu-karmic-fail/">failure to sync directly</a>. Let's not go there.</li>
<li>How about just syncing KOrganizer with Google, and syncing the BlackBerry with Google?  I already have some calendars on Google, and that works fine.  But now for my main calender:<ul>
<li>I tried GCalDaemon, but found that it <a class="reference external" href="https://sourceforge.net/tracker/?func=detail&amp;aid=2844153&amp;group_id=184382&amp;atid=909032">ignores/deletes all recurring events on your calendar</a>.  Which is pretty evil.</li>
<li>There is Akonadi... Given how hopelessly buggy Akonadi is for syncing contacts to Google (like, if Google has more than one e-mail for a contact, none of them are transferred, and worse), I'm not very hopeful about this (and I'm using most recent KDE 4.4.1).  But I'll try anyway...<ul>
<li>The whole experience is fraught with bugs and confusion.  You click 'Add' and things aren't added, you click 'Remove' and they don't disappear, or only after some indefinite period of time (I've waited 30 seconds, is it just broken or should I carry on waiting?).  You close the Akonadi configuration and open it again, or restart the server, and items have appeared, and other things that you didn't ask for.  A lot seems to go on asynchronously, which might be a good idea, but you get the impression you are attempting to communicate with a forgetful, deaf, sleepy, indecisive, incompetent idiot.  I have eventually managed to add a Google calendar resource, but it says &quot;Failed getting last updated event&quot;, and nothing appears on my calendar.</li>
</ul>
</li>
</ul>
</li>
<li>OK, let's try Evolution calendar.  At first it didn't work, then it seemed to work a bit better, but trying to modify events returns &quot;Unknown error&quot;, and now it refuses to show the calendar at all.</li>
<li>OK, let's just try using Google calendar, importing all my existing calendar data, ditching any hope of using a local calendar app.  But Google doesn't like my calendar file - &quot;Failed to import events: Processing error during upload.&quot;</li>
</ul>
<p>Epic fail.  Bad day.</p>
<hr class="docutils" />
<p>Update: in the last case, I discovered after some investigation that Google doesn't like the 'VTIMEZONE' sections in my calendar file.  Removing them allows the import to succeed (although it still produces an error message, and I'm guessing it might have lost some time information on summer events though, not sure).</p>
</div>
]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[Blackberry Curve ↔ KDE-PIM sync on Kubuntu Karmic. FAIL]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/blackberry-curve-kde-pim-sync-on-kubuntu-karmic-fail/" />
    <id>http://lukeplant.me.uk/blog/posts/blackberry-curve-kde-pim-sync-on-kubuntu-karmic-fail/</id>
    <updated>2010-02-11T17:07:58Z</updated>
    <published>2010-02-11T17:07:58Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="KDE" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[Blackberry Curve ↔ KDE-PIM sync on Kubuntu Karmic. FAIL]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/blackberry-curve-kde-pim-sync-on-kubuntu-karmic-fail/"><![CDATA[<div class="document">
<p>For the sake of anyone else who might want to know about this in advance, I'll blog my failure.</p>
<p>I tried with Kubuntu Karmic, and the following steps:</p>
<ol class="arabic">
<li><p class="first">Install the necessary packages:</p>
<pre class="code bash literal-block">
sudo aptitude install opensync-plugin-kdepim opensyncutils barrybackup-gui barry-util opensync-plugin-barry
</pre>
</li>
<li><p class="first">Plug in the Blackberry, and choose &quot;Mass storage&quot; mode on the Blackberry.</p>
</li>
<li><p class="first">Set up permissions to allow a non-root user to access the device.  I added the file <tt class="docutils literal"><span class="pre">/etc/udev/rules.d/040-devices.rules</span></tt>, with the following contents:</p>
<pre class="literal-block">
BUS==&quot;usb&quot;, SYSFS{idVendor}==&quot;0fca&quot;, SYSFS{idProduct}==&quot;8004&quot;, GROUP=&quot;admin&quot;
</pre>
<p>The above line gives permissions to all users of group 'admin', which includes my normal user.  The numbers &quot;0fca&quot; and &quot;8004&quot; are device numbers obtainable from <tt class="docutils literal">lsusb</tt>:</p>
<pre class="code bash literal-block">
lsusb | grep <span class="s2">&quot;Research In Motion&quot;</span>
</pre>
<p>Result:</p>
<pre class="code bash literal-block">
Bus 002 Device 008: ID 0fca:8004 Research In Motion, Ltd.
</pre>
<p>You will need to unplug the Blackberry and plug back in after this step.</p>
</li>
<li><p class="first">Check you can access the Blackberry.</p>
<pre class="code bash literal-block">
btool -t
</pre>
<p>This should list a series of databases on the Blackberry</p>
</li>
<li><p class="first">Back up the current contents:</p>
<pre class="code bash literal-block">
barrybackup
</pre>
<p>Note the pin for later, and follow the prompts to backup the Blackberry data.</p>
</li>
<li><p class="first">Back up your KDE-PIM data, in case of any corruption, duplicate elements etc. (It happened to me!).</p>
</li>
<li><p class="first">Create a msynctool configuration for doing the synchronisation.</p>
<pre class="code bash literal-block">
msynctool --addgroup Blackberry
msynctool --addmember Blackberry barry-sync
msynctool --addmember Blackberry kdepim-sync
msynctool --showgroup Blackberry
</pre>
<p>The final command should show the first member as missing configuration. Do the following</p>
<pre class="code bash literal-block">
msynctool --configure Blackberry 1
</pre>
<p>and insert your pin in the relevant place.</p>
</li>
<li><p class="first">Moment of truth.  Close KOrganizer and KMail, then:</p>
<pre class="code bash literal-block">
msynctool --sync Blackberry
</pre>
</li>
</ol>
<p>I came across the following major bugs:</p>
<ul class="simple">
<li>Syncing never completes, and I have to press Ctrl-C to abort.</li>
<li>When I try to sync a second/third time, I end up with lots of duplicates on my computer, caused by items from the Blackberry being copied back.  I'm not sure if they are to do with the unclean abort, or the following symptoms:
* 'All day events'.  A duplicate appears with the start time set to '0:00'
* Daylight savings problems - for summer events, a duplicate appears 1 hour different from the original
* Items from my Google calendar (which is a read only calendar) got copied back to my main calendar.</li>
<li>Each time I attempt to sync, duplicate events and contacts build up on the Blackberry as well.</li>
<li>Using barrybackup to restore to a clean state with the backup made previously fails with lots of errors.  By removing some items in <tt class="docutils literal"><span class="pre">~/.barry/backup/&lt;pin&gt;/config</span></tt>, I've got it to apparently succeed, but the contacts and calendar on the Blackberry are not cleared out, which is the point of the exercise.
* There is a way to clear calendar/contacts on the Blackberry.  Go to Calendar -&gt; Menu -&gt; Options.  Type RSET.  The calendar will be cleared (though it says nothing).  Repeat for contacts.  This time you will get a prompt about clearing.  Clearing out the msynctool database is also a good idea to keep further attempts  clean and fast.</li>
</ul>
<p>In the end, a one-time, one-way sync is all that I have been able to achieve.  That is quite disappointing, since none of the web pages I found before deciding on a Blackberry mentioned any problems like these.</p>
<p>I tried upgrading all the barry packages from 0.14 to 0.16, but got lots more errors - when trying to send data to the Blackberry I just got &quot;Timeout in usb_bulk_read&quot; blah blah and do data was actually sent.</p>
<p>Sources:</p>
<ul class="simple">
<li><a class="reference external" href="http://forums.debian.net/viewtopic.php?t=20424">Contacts, sms messages and files from and to mobile phone</a></li>
<li><a class="reference external" href="http://www.linux.com/news/embedded-mobile/mids/8210-syncing-your-blackberry-on-linux">Syncing your BlackBerry on Linux</a></li>
</ul>
</div>
]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[Python and copyright]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/python-and-copyright/" />
    <id>http://lukeplant.me.uk/blog/posts/python-and-copyright/</id>
    <updated>2009-07-15T22:04:42Z</updated>
    <published>2009-07-15T22:04:42Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="Software development" />
    <category scheme="http://lukeplant.me.uk/blog" term="Python" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[Python and copyright]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/python-and-copyright/"><![CDATA[
<div class="document">


<p>There has been lots of <a class="reference external" href="http://jacobian.org/writing/gpl-questions/">interesting discussion</a> about dynamic languages and the
GPL, but much of it seems to miss the main point: with many dynamic languages,
the terms of the GPL are an irrelevance.</p>
<p>In the comments on the above page, Peter Gerdes seems to be one of the few who
does get this:</p>
<blockquote>
<p>@Morty Yidel</p>
<p>It doesn't matter what the GPL says in situations 2 &amp; 3. What matters here is
copyright law.</p>
<p>The GPL is a license that lets you create and distribute copies of certain
software. You are only bound by the terms of that license when you need the
license as a matter of copyright law.</p>
<p>In 2 &amp; 3 you aren't distributing any GPLed software thus you need only abide
by the GPL if it is a derivitive work of a GPLed program. As I said that's a
complex question but it's one that can only be answered by looking at
copyright law and precedent not the GPL. Moreover, if merely using an API
makes your code a derivitive work of the library servicing the API it's hard
to see how programs like WINE aren't infringing.</p>
</blockquote>
<p>If someone wants to apply the terms of the GPL, they <strong>first</strong> have to demonstrate
something that would otherwise be copyright infringement. As the GPL (v2) says:</p>
<blockquote>
5. You are not required to accept this License, since you have not signed it.
However, nothing else grants you permission to modify or distribute the
Program or its derivative works.  These actions are prohibited by law if you
do not accept this License.  Therefore, by modifying or distributing the
Program (or any work based on the Program), you indicate your acceptance of
this License to do so, and all its terms and conditions for copying,
distributing or modifying the Program or works based on it.</blockquote>
<p>So, if I sit down at my computer and type (for the sake of argument):</p>
<pre class="literal-block">
echo 'import qt' &gt; test.py
</pre>
<p>and then distribute test.py, have I created a derivative work of Qt (or any
other GPL library)?  Since I could type all of that without having a drop of Qt
source code on my computer, I'd argue it's pretty difficult to say that this is
a derivative work.  Of course, once I go further and actually start using more
the API than just the name 'qt', there is the chance that someone important
might decide that by virtue of my use of an API, the program constitutes a
derivative work of the library.  (If they did, that would have huge implications
— every Linux program, not to mention Linux itself, would become a derivative of
Unix itself, for example, because it uses Unix API's, and every Win32 program
would be a Windows derivative...)</p>
<p>This, of course, is completely different from C/C++, where in order to compile
an executable, the compiler #includes parts of the actual source code of the
library into the executable, thereby automatically triggering the need for an
exemption from copyright laws.</p>
<p>With Python and Javascript, nothing of the sort occurs for the user to get the
developer's software.  If the developer decides to test their program before
distributing, they may well need copies of the library code, either in source or
object form, on their machine, but that has nothing to do with their
distribution of their own code.</p>
<p>Now, the GPL (or whatever), may have terms explicitly forbidding you from
finding some sneaky way around the licence, such as dynamic languages or web
interfaces etc.  But it doesn't matter.  The licence could require you to
sacrifice your first-born child to the gods of Jupiter for even thinking of
doing such a thing, but the licence is irrelevant, since you haven't done
anything that would require you to accept its terms.</p>
<p>So, unless judges are going to rule that use of APIs constitutes copyright
infringement, <strong>all</strong> software licenses of this kind are simply irrelevant in
this case.</p>
<p>This is one of the things that makes me shy away from the GPL.  I'm a really
grateful beneficiary of all that it has helped to safeguard, but as it relies on
copyright law which may well be <em>completely irrelevant</em> for dynamic languages,
it seems somewhat unwise to lean on it for any of my own financial security.
More and more it seems that being paid for <em>writing</em> code, and not for the <em>code
itself</em>, is the way programmers should make their living.  Just because
copyright law may have supported certain practices in the past doesn't mean we
have a right to that continuing.</p>
</div>

]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[MOC and last.fm]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/moc-and-last-fm/" />
    <id>http://lukeplant.me.uk/blog/posts/moc-and-last-fm/</id>
    <updated>2008-06-26T22:26:11Z</updated>
    <published>2008-06-26T22:26:11Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="Personal and misc" />
    <category scheme="http://lukeplant.me.uk/blog" term="Music" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[MOC and last.fm]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/moc-and-last-fm/"><![CDATA[<div class="document">
<p>Update: people are having trouble with this now, I am unable to debug what is wrong, it seems like a problem with mocp.</p>
<hr class="docutils" />
<p>When not using Amarok (e.g. if I'm low on memory due to using VMs
or something), <a class="reference external" href="http://moc.daper.net/">moc</a> is very nice music
player (Debian/Ubuntu package 'moc', but command 'mocp'). Here is
how to get it to submit tracks to <a class="reference external" href="http://www.last.fm/">last.fm</a>
in a well behaved manner (that doesn't submit tracks if you are
just skipping through a playlist):</p>
<ul class="simple">
<li>Install lastfmsubmitd: <tt class="docutils literal">sudo aptitude install lastfmsubmitd</tt></li>
<li>Add yourself to lastfm group:
<tt class="docutils literal">sudo adduser [yourusername] lastfm</tt></li>
<li>Download and save my
<a class="reference external" href="http://files.lukeplant.fastmail.fm/public/moc_submit_lastfm">MOC - last.fm script</a>,
and make it executable.</li>
<li>Alter your ~/.moc/config to set OnSongChange as per the
instructions at the top of the script.</li>
<li>Completely quit moc and restart it.</li>
</ul>
<p>The advantage of the script I wrote over just using OnSongChange is
that my script will wait for half the length of the song before
submitting to last.fm. Every 5 seconds it will check you are still
listening to that song, and if not, it will quit. This way it will
behave much more like Amarok and other well behaved last.fm
clients.</p>
</div>
]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://lukeplant.me.uk/blog</uri>
    </author>
    <title type="html"><![CDATA[Personal wiki and desktop integration]]></title>
    <link rel="alternate" type="text/html" href="http://lukeplant.me.uk/blog/posts/personal-wiki-and-desktop-integration/" />
    <id>http://lukeplant.me.uk/blog/posts/personal-wiki-and-desktop-integration/</id>
    <updated>2008-02-23T21:12:57Z</updated>
    <published>2008-02-23T21:12:57Z</published>
    <category scheme="http://lukeplant.me.uk/blog" term="Python" />
    <category scheme="http://lukeplant.me.uk/blog" term="KDE" />
    <category scheme="http://lukeplant.me.uk/blog" term="Linux" />
    <summary type="html"><![CDATA[Personal wiki and desktop integration]]></summary>
    <content type="html" xml:base="http://lukeplant.me.uk/blog/posts/personal-wiki-and-desktop-integration/"><![CDATA[
<P>
Like many people, I find it easier to find stuff on the web than on my own machine.  This post is about some of my attempts to remedy that.  You probably need to be a developer or that way inclined to get the most out of some of the solutions I've got here.  Stuff to download is at the bottom.
</P>
<H2 id="Searchtool">Search tool<A class="anchor" href="#Searchtool" title="Link to this section"> §</A></H2>
<P>
Obviously, first of all there is a need for a decent desktop search tool.  On Linux, your main options are:
</P>
<UL><LI><A class="ext-link" href="http://desktop.google.com/linux/"><SPAN class="icon">Google desktop search</SPAN></A>
<UL><LI>Main problem: it stops it's initial index after 100,000 documents.  I've got far more than that, and it means it never actually reaches my mail.  Typical crippleware.  This is useless for me.  
</LI></UL></LI><LI><A class="ext-link" href="http://beagle-project.org/"><SPAN class="icon">Beagle</SPAN></A>
<UL><LI>Main problem:  it will decide to thrash your disk and/or CPU and crash.  For me, runaway CPU usage and crashing made this completely useless.  It might be 99% of the way to being brilliant, but until these things are sorted out, it is a 100% waste of time.
</LI></UL></LI><LI><A class="ext-link" href="http://strigi.sourceforge.net/"><SPAN class="icon">Strigi</SPAN></A>
<UL><LI>Minor problem:  the GUIs suck
</LI><LI>Minor problem:  it indexes too much, sometimes.
</LI><LI>Main problem: after indexing about 300,000 documents, Strigi dies on me and refuses to do anything useful at all.  I don't know what causes it to choke, but it, too, is 100% useless AFAIC.  Maybe it's fixed now, but I've got bored of filing bugs and compiling from source just to find out it still doesn't work.
</LI></UL></LI><LI><A class="ext-link" href="http://www.gnome.org/projects/tracker/"><SPAN class="icon">MetaTracker</SPAN></A>, or Tracker for short.
<UL><LI>It can't search zip files etc. like Strigi can, which is a shame.
</LI><LI>It doesn't yet index my emails (KMail) -- it only does evolution.
</LI><LI>But I actually find this tool useful, .  It doesn't thrash my disk, it has a simple but effective GUI, as well as command line tools, it integrates <I>reasonably</I> well into KDE, and it also has a nice tagging facility.
</LI></UL></LI></UL><P>
   
</P>
<H2 id="TaggingandKonqueror">Tagging and Konqueror<A class="anchor" href="#TaggingandKonqueror" title="Link to this section"> §</A></H2>
<P>
Adding tags to files needs to be easy to do if it's going to happen.  MetaTracker provides the <TT>tracker-tag</TT> program which works well, but it is command line.  So I've written a small GUI wrapper that integrates into Konqueror via a service menu.  If you tag more than one file at once (by selecting a few files in Konqueror), it is in bulk tag mode, where it pops up an input box and simple adds the space-separated tags you enter to all the files.  
</P>
<P>
In single file mode, it does something slightly different.  It will first query the file for any existing tags, adding them to the default text that is in the input box, so that you can remove tags as well as add new ones.  It will also add a suggestion for an 'ID' tag -- based on the filename, with punctuation replaced by hyphens, and with 'ID-' prepended.  The idea is that I edit this and make it into something that will uniquely identify that file.  For example, a file "bible reading schedule.pdf" ends up with the tag ID-bible-reading-schedule-pdf, but "rest-quickref2.html" I will expand to "ID-restructured-text-quickref-html", and in some cases I would have to include something else that will distinguish it.
</P>
<P>
The nice thing about these tags is that MetaTracker will keep track of them <STRONG>even if I move or rename the file</STRONG>.  I'm not entirely sure how it does this (I think it uses inotify).  But it works, and that's all I care about for now.
</P>
<H3 id="Migrating">Migrating<A class="anchor" href="#Migrating" title="Link to this section"> §</A></H3>
<P>
It may be that strigi / Nepomuk will mature and become useful.  When they do, I can dump the tag database from MetaTracker pretty easily, and should be able to import into strigi.  Changing the tools/scripts I've developed to use a different metadata database should be pretty simple.
</P>
<H2 id="Personalwiki">Personal wiki<A class="anchor" href="#Personalwiki" title="Link to this section"> §</A></H2>
<P>
On top of this, there are other things that file systems are just not very helpful for organising.  Hierarchy works pretty well for about half my stuff (development work in particular).  But notes and things I like to jot down often get lost on my disk -- there are multiple places that some things belong, and some belong nowhere at all.
</P>
<P>
I decided that a personal wiki would be the best way forward for this.  However, I then had to choose a wiki -- thankfully the <A class="ext-link" href="http://www.wikimatrix.org/"><SPAN class="icon">WikiMatrix</SPAN></A> helps a lot.  I had tried the <A class="ext-link" href="http://www.instiki.org/show/HomePage"><SPAN class="icon">Instiki</SPAN></A> wiki.  But it turns out that Ruby on Rails <I>does</I> suck as much as they say -- the instiki process would sit there at about 50Mb for a while, but then decide that it would <A class="ext-link" href="http://rubyforge.org/tracker/index.php?func=detail&amp;aid=18185&amp;group_id=186&amp;atid=783"><SPAN class="icon">use up all my memory</SPAN></A>.  (Other people have noticed the same behaviour). So I wasted some time with that, and I don't think I'll be trying Ruby on Rails soon if this is the kind of thing I can expect.  
</P>
<P>
But anyway, WikiMatrix helped me find the <A class="ext-link" href="http://trac.edgewall.org/"><SPAN class="icon">Trac</SPAN></A> wiki.  Though I've used Trac a lot before, I hadn't thought of this option, as it is a wiki that is geared towards software projects, not general things.  However, I've discovered it is ideal for me:
</P>
<UL><LI>You can very easily turn off all the stuff you don't want, and then those components just disappear from the interface, exactly like they should.
</LI><LI>It has all the help you need built in.
</LI><LI>Nice GUI for administering it -- you need to use the command line UI for the first few steps, and you need to use htdigest to set up a password file, which some people might find tricky, but as a developer I'm right at home with that kind of thing.
</LI><LI>It is written in Python, and it seems to be very well written too.
</LI><LI>It uses SQlite (can use other databases), so it easy to query from outside if necessary.
</LI><LI>It comes with a built in server -- very little setup, easy to run as a normal user.
</LI><LI>It has a plugin architecture with <A class="ext-link" href="http://trac.edgewall.org/wiki/PluginList"><SPAN class="icon">lots of good plugins</SPAN></A>, and you can easily write your own.
</LI></UL><P>
This has allowed me to bring lots of bits and pieces into one place, and organise them as loosely or tightly as I want. The <A class="ext-link" href="http://trac-hacks.org/wiki/TagsPlugin"><SPAN class="icon">TagsPlugin</SPAN></A> for Trac makes this particularly convenient.  The wiki is actually pretty nice for composing blog posts too -- it can do all the code highlighting I want, and I can just copy the output HTML into my blog when it's ready.
</P>
<H2 id="Combiningwikianddesktop">Combining wiki and desktop<A class="anchor" href="#Combiningwikianddesktop" title="Link to this section"> §</A></H2>
<P>
The final problem is integrating wiki and other files.  Obviously, I have many documents that are much better managed outside a wiki.  It would be nice to be able to refer to these easily.  And by writing some Trac plugins, this is actually pretty easy.  I can now write things like this:
</P>
<PRE class="wiki">  file:ID-bible-reading-schedule-pdf
</PRE><P>
in my wiki and it turns into a <TT>file:///</TT> link to the right document.  In Konqueror, this works really well -- right clicking on the link brings up the same context menu that I would get in file browsing mode, so everything is available to me right there from the wiki page.  I can also do a search:
</P>
<PRE class="wiki">  [[MetaTrackerSearch(programming languages,web)]]
</PRE><P>
This will returns links to all files containing the text "programming languages" and "web", nicely formatted in a table.  Or I can do a tag search like this:
</P>
<PRE class="wiki">  [[MetaTrackerTagSearch(sermon)]]
</PRE><P>
to return all my sermons.
</P>
<H2 id="Further">Further<A class="anchor" href="#Further" title="Link to this section"> §</A></H2>
<P>
I've created a very simple Trac plugin for creating <A class="ext-link" href="http://lukeplant.me.uk/kio-sword/"><SPAN class="icon">KioSword</SPAN></A> links, which works nicely for embedding links to Bible passages.  I've got some ideas for further integrating different parts of my desktop.  One thing I'd like to do is produce a database of my sermons and bible studies, the texts they are on, when and where I've preached them etc.  A private Django app using the Django admin would probably work exceedingly well, and be very little work.  A simple export routine could produce a wiki page that could be 'included' into my wiki (using a Trac macro).  It could also export an read only ICal file which can be displayed in Kontact.  So many cool ideas, so little time...
</P>
<H2 id="Downloadlinks">Download links<A class="anchor" href="#Downloadlinks" title="Link to this section"> §</A></H2>
<UL><LI><A class="ext-link" href="http://trac-hacks.org/wiki/MetaTrackerPluginMacro"><SPAN class="icon">MetaTrackerPluginMacro</SPAN></A>
</LI></UL><UL><LI><A class="ext-link" href="http://files.lukeplant.fastmail.fm/public/tracker_tag_file"><SPAN class="icon">tracker_tag_file</SPAN></A> <BR>
Install this by putting it in your path somewhere (e.g. ~/bin, if that is on your PATH) <BR>
It requires:
<UL><LI>kdialog (present on all KDE installations)
</LI><LI>setClipboard -- a little utility to set the clipboard with the contents of the tags just added, for convenience only, see below.<BR>
</LI></UL></LI></UL><UL><LI><A class="ext-link" href="http://files.lukeplant.fastmail.fm/public/setClipboard"><SPAN class="icon">setClipboard</SPAN></A>
</LI></UL><UL><LI><A class="ext-link" href="http://files.lukeplant.fastmail.fm/public/tagfile.desktop"><SPAN class="icon">tagfile.desktop</SPAN></A> <BR>
Install into ~/.kde/share/apps/konqueror/servicemenus/
</LI></UL>

]]></content>
  </entry>
</feed>
