<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>t+1 &#187; pitz</title>
	<atom:link href="http://blog.tplus1.com/index.php/category/pitz/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.tplus1.com</link>
	<description>Programming, gardening, economics, life in Cleveland Heights</description>
	<lastBuildDate>Sat, 07 Jan 2012 21:12:21 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>A new pitz release (1.1.2)</title>
		<link>http://blog.tplus1.com/index.php/2010/10/04/a-new-pitz-release-1-1-2/</link>
		<comments>http://blog.tplus1.com/index.php/2010/10/04/a-new-pitz-release-1-1-2/#comments</comments>
		<pubDate>Tue, 05 Oct 2010 03:20:43 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=647</guid>
		<description><![CDATA[I&#8217;m using semantic versioning now, so I bumped from 1.0.7 to 1.1.0 after adding a few tweaks to the command-line interface.  Then I discovered a silly mistake in the setup.py file, and released 1.1.1.  Then I realized I did the fix wrong, and then released 1.1.2 a few minutes later.
The &#8211;quick option
Normally, running [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m using <a href="http://semver.org">semantic versioning</a> now, so I bumped from 1.0.7 to 1.1.0 after adding a few tweaks to the command-line interface.  Then I discovered a silly mistake in the setup.py file, and released 1.1.1.  Then I realized I did the fix wrong, and then released 1.1.2 a few minutes later.</p>
<h2>The &#8211;quick option</h2>
<p>Normally, running <b><code>pitz-add-task</code></b> will prompt for a title and then open $EDITOR so I can write a description.  After that, pitz will ask for choices for milestone, owner, status, estimate, and tags.</p>
<p>Sometimes I want to make a quick task without getting prompted for all this stuff.</p>
<p>I already had a <b><code>--no-description</code></b> option that would tell <b><code>pitz-add-task</code></b> to not open $EDITOR for a description.  And I already had a <b><code>--use-defaults</code></b> option to just choose the default values for milestone, owner, status, estimate, and tags.</p>
<p>But when I just want to make a quick to-do task as a placeholder, writing out all this stuff:<pre><code>$ pitz-add-task --no-description --use-defaults -t &quot;Fix fibityfoo&quot;
</code></pre></p>
<p>is kind of a drag.  So I made a <b><code>--quick</code></b> option (also available as <b><code>-q</code></b>) that does the same thing as <b><code>--no-description --use-defaults</code></b>.</p>
<h2>The -1 alias for &#8211;one-line-view</h2>
<p>This is the typical way that a to-do list looks:<pre><code>$ pitz-my-todo -n 3
==============================
slice from To-do list for matt
==============================

(3 task entities, ordered by [&#039;milestone&#039;, &#039;status&#039;, &#039;pscore&#039;])

Write new CLI scripts (witz!) to talk to pitz-webapp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9f1c76
matt | paused | difficult | 1.0 | 1
webapp, CLI
Starting up and loading all the pitz data at the beginning of ever...

Experiment with different task summarized views&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0f6fee
matt | unstarted | straightforward | 1.0 | 1
CLI
Right now, the summarized view of a task looks a little like this:...

Add more supported URLs to pitz-webapp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 295b5f
matt | unstarted | straightforward | 1.0 | 0
webapp
I want to allow these actions through the webapp:&nbsp;&nbsp;*&nbsp;&nbsp; Insert a ne...
</code></pre></p>
<p>Incidentally, notice the <b><code>-n 3</code></b> option limits the output to the first three tasks.</p>
<p>Tasks also have a one-line view:<pre><code>$ pitz-my-todo -n 3 --one-line-view
==============================
slice from To-do list for matt
==============================

(3 task entities, ordered by [&#039;milestone&#039;, &#039;status&#039;, &#039;pscore&#039;])

Write new CLI scripts (witz!) to talk to pitz-webapp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9f1c76
Experiment with different task summarized views&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0f6fee
Add more supported URLs to pitz-webapp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 295b5f
</code></pre></p>
<p>Typing out <b><code>--one-line-view</code></b> is tedious, so now, <b><code>-1</code></b> is an alias that works as well:<pre><code>$ pitz-my-todo -n 3 -1
==============================
slice from To-do list for matt
==============================

(3 task entities, ordered by [&#039;milestone&#039;, &#039;status&#039;, &#039;pscore&#039;])

Write new CLI scripts (witz!) to talk to pitz-webapp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9f1c76
Experiment with different task summarized views&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0f6fee
Add more supported URLs to pitz-webapp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 295b5f
</code></pre></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2010/10/04/a-new-pitz-release-1-1-2/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>pitz 1.0.5 now supports bash colorization</title>
		<link>http://blog.tplus1.com/index.php/2010/03/22/pitz-1-0-5-now-supports-bash-colorization/</link>
		<comments>http://blog.tplus1.com/index.php/2010/03/22/pitz-1-0-5-now-supports-bash-colorization/#comments</comments>
		<pubDate>Mon, 22 Mar 2010 21:09:14 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=572</guid>
		<description><![CDATA[Today is day one of my week off.  The rainy weather kept me inside, so I added a bash coloring to pitz.  The screen shot below shows an example:
It took me a while to figure out how to include the jinja2 template files with the package, but you can now download pitz from [...]]]></description>
			<content:encoded><![CDATA[<p>Today is day one of my week off.  The rainy weather kept me inside, so I added a bash coloring to pitz.  The screen shot below shows an example:</p>
<div id="attachment_571" class="wp-caption alignleft" style="width: 672px"><a href="http://blog.tplus1.com/wp-content/uploads/2010/03/pitz-bash-colorization.png"><img src="http://blog.tplus1.com/wp-content/uploads/2010/03/pitz-bash-colorization.png" alt="screenshot" title="pitz-bash-colorization" width="662" height="435" class="size-full wp-image-571" /></a><p class="wp-caption-text">pretty colors</p></div>
<p>It took me a while to figure out how to include the jinja2 template files with the package, but you can now download pitz from PyPi <a href="http://pypi.python.org/pypi/pitz">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2010/03/22/pitz-1-0-5-now-supports-bash-colorization/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>pitz has a CLI</title>
		<link>http://blog.tplus1.com/index.php/2009/11/17/pitz-has-a-cli/</link>
		<comments>http://blog.tplus1.com/index.php/2009/11/17/pitz-has-a-cli/#comments</comments>
		<pubDate>Wed, 18 Nov 2009 02:32:08 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=514</guid>
		<description><![CDATA[I&#8217;ve exposed lots and lots of pitz functionality as command-line scripts.  Here&#8217;s the list so far:

$ pitz-help
&#160;&#160;&#160;&#160;pitz-abandon-task&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Abandon a task
&#160;&#160;&#160;&#160;pitz-add-task&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Walks through the setup of a new Task.
&#160;&#160;&#160;&#160;pitz-components&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;All components in the project
&#160;&#160;&#160;&#160;pitz-estimates&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; All estimates in the project
&#160;&#160;&#160;&#160;pitz-everything&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;No description
&#160;&#160;&#160;&#160;pitz-finish-task&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Finish a task
&#160;&#160;&#160;&#160;pitz-milestones&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;No description
&#160;&#160;&#160;&#160;pitz-my-tasks&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;List my tasks
&#160;&#160;&#160;&#160;pitz-people&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;All people in the project
&#160;&#160;&#160;&#160;pitz-prioritize-above&#160;&#160;&#160;&#160;&#160;&#160;Put one task in front of another task
&#160;&#160;&#160;&#160;pitz-prioritize-below&#160;&#160;&#160;&#160;&#160;&#160;Put one [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve exposed lots and lots of pitz functionality as command-line scripts.  Here&#8217;s the list so far:<br />
<pre><code>
$ pitz-help
&nbsp;&nbsp;&nbsp;&nbsp;pitz-abandon-task&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Abandon a task
&nbsp;&nbsp;&nbsp;&nbsp;pitz-add-task&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Walks through the setup of a new Task.
&nbsp;&nbsp;&nbsp;&nbsp;pitz-components&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;All components in the project
&nbsp;&nbsp;&nbsp;&nbsp;pitz-estimates&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; All estimates in the project
&nbsp;&nbsp;&nbsp;&nbsp;pitz-everything&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;No description
&nbsp;&nbsp;&nbsp;&nbsp;pitz-finish-task&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Finish a task
&nbsp;&nbsp;&nbsp;&nbsp;pitz-milestones&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;No description
&nbsp;&nbsp;&nbsp;&nbsp;pitz-my-tasks&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List my tasks
&nbsp;&nbsp;&nbsp;&nbsp;pitz-people&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;All people in the project
&nbsp;&nbsp;&nbsp;&nbsp;pitz-prioritize-above&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put one task in front of another task
&nbsp;&nbsp;&nbsp;&nbsp;pitz-prioritize-below&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Put one task behind another task
&nbsp;&nbsp;&nbsp;&nbsp;pitz-recent-activity&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10 recent activities
&nbsp;&nbsp;&nbsp;&nbsp;pitz-setup&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; No description
&nbsp;&nbsp;&nbsp;&nbsp;pitz-shell&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Start an ipython session after loading in a ...
&nbsp;&nbsp;&nbsp;&nbsp;pitz-show&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Show detailed view of one entity
&nbsp;&nbsp;&nbsp;&nbsp;pitz-start-task&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Begin a task
&nbsp;&nbsp;&nbsp;&nbsp;pitz-statuses&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;All statuses in the project
&nbsp;&nbsp;&nbsp;&nbsp;pitz-tasks&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; All tasks in the project
&nbsp;&nbsp;&nbsp;&nbsp;pitz-todo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List every unstarted and started task in the...
&nbsp;&nbsp;&nbsp;&nbsp;pitz-unassign-task&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Take this task off somebody&#039;s list of stuff ...
</code></pre></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2009/11/17/pitz-has-a-cli/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Help me rewrite some repetitive scripts</title>
		<link>http://blog.tplus1.com/index.php/2009/10/18/help-me-rewrite-some-repetitive-scripts/</link>
		<comments>http://blog.tplus1.com/index.php/2009/10/18/help-me-rewrite-some-repetitive-scripts/#comments</comments>
		<pubDate>Sun, 18 Oct 2009 23:36:18 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=489</guid>
		<description><![CDATA[I have about a dozen functions (that are run as scripts) that have very similar sections interleaved with specialized code.  I copied two of the scripts below.  Here&#8217;s the first:
def pitz_estimate_task():

&#160;&#160;&#160;&#160;p = optparse.OptionParser()

&#160;&#160;&#160;&#160;p.add_option(&#039;--version&#039;, action=&#039;store_true&#039;,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;help=&#039;Print the version and exit&#039;)

&#160;&#160;&#160;&#160;# This script requires these arguments.
&#160;&#160;&#160;&#160;p.set_usage(&#34;%prog task [estimate]&#34;)
&#160;&#160;&#160;&#160;options, args = p.parse_args()
&#160;&#160; 
&#160;&#160;&#160;&#160;if options.version:
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;print_version()
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;return

&#160;&#160;&#160;&#160;# This is unique [...]]]></description>
			<content:encoded><![CDATA[<p>I have about a dozen functions (that are run as scripts) that have very similar sections interleaved with specialized code.  I copied two of the scripts below.  Here&#8217;s the first:<br />
<pre><code>def pitz_estimate_task():

&nbsp;&nbsp;&nbsp;&nbsp;p = optparse.OptionParser()

&nbsp;&nbsp;&nbsp;&nbsp;p.add_option(&#039;--version&#039;, action=&#039;store_true&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;help=&#039;Print the version and exit&#039;)

&nbsp;&nbsp;&nbsp;&nbsp;# This script requires these arguments.
&nbsp;&nbsp;&nbsp;&nbsp;p.set_usage(&quot;%prog task [estimate]&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;options, args = p.parse_args()
&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;if options.version:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print_version()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return

&nbsp;&nbsp;&nbsp;&nbsp;# This is unique to this script.
&nbsp;&nbsp;&nbsp;&nbsp;if not args:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.print_usage()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return

&nbsp;&nbsp;&nbsp;&nbsp;# And now we&#039;re back to boring generic stuff.
&nbsp;&nbsp;&nbsp;&nbsp;pitzdir = Project.find_pitzdir(options.pitzdir)
&nbsp;&nbsp;&nbsp;&nbsp;proj = Project.from_pitzdir(pitzdir)
&nbsp;&nbsp;&nbsp;&nbsp;proj.find_me()

&nbsp;&nbsp;&nbsp;&nbsp;# This section is specific to this script.
&nbsp;&nbsp;&nbsp;&nbsp;t = proj[args[0]]
&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;if len(args) == 2:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;est = proj[args[1]]
&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;else:&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;est = Estimate.choose_from_already_instantiated()
&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;t[&#039;estimate&#039;] = est
&nbsp;&nbsp;&nbsp;&nbsp;# That was the last thing that was specific to just this script.

&nbsp;&nbsp;&nbsp;&nbsp;# Save the project (generic).
&nbsp;&nbsp;&nbsp;&nbsp;proj.save_entities_to_yaml_files()
</code></pre></p>
<p>That script does some &#8220;generic&#8221; stuff to build an object p, then adds on some extra tweaks to p, and uses p to build an options object and an args object. </p>
<p>Then the script does some generic stuff to build a proj object based on the data in the options.pitzdir object, and does some various method calls on the proj object.</p>
<p>And here&#8217;s another script:<br />
<pre><code>def pitz_attach_file():

&nbsp;&nbsp;&nbsp;&nbsp;p = optparse.OptionParser()

&nbsp;&nbsp;&nbsp;&nbsp;p.add_option(&#039;--version&#039;, action=&#039;store_true&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;help=&#039;Print the version and exit&#039;)

&nbsp;&nbsp;&nbsp;&nbsp;# Notice this line is different than the one in pitz_estimate_task.
&nbsp;&nbsp;&nbsp;&nbsp;p.set_usage(&quot;%prog entity file-to-attach&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;options, args = p.parse_args()

&nbsp;&nbsp;&nbsp;&nbsp;if options.version:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print_version()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return

&nbsp;&nbsp;&nbsp;&nbsp;# This section is different too.
&nbsp;&nbsp;&nbsp;&nbsp;if len(args) != 2:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.print_usage()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return

&nbsp;&nbsp;&nbsp;&nbsp;# Back to the generic code to build the project.
&nbsp;&nbsp;&nbsp;&nbsp;pitzdir = Project.find_pitzdir(options.pitzdir)
&nbsp;&nbsp;&nbsp;&nbsp;proj = Project.from_pitzdir(pitzdir)
&nbsp;&nbsp;&nbsp;&nbsp;proj.find_me()

&nbsp;&nbsp;&nbsp;&nbsp;# Some interesting stuff that is specific just for this script.
&nbsp;&nbsp;&nbsp;&nbsp;e, filepath = proj[args[0]], args[1]
&nbsp;&nbsp;&nbsp;&nbsp;e.save_attachment(filepath)

&nbsp;&nbsp;&nbsp;&nbsp;# Save the project. (Generic).
&nbsp;&nbsp;&nbsp;&nbsp;proj.save_entities_to_yaml_files()
</code></pre></p>
<p>So, the pattern in every script is: generic code, specific code, generic code, specific code, generic code.  And each step depends on the previous step.</p>
<p>I know I could do stuff like wrap all the generic stuff into functions, but I&#8217;m not really a fan of that approach.  I&#8217;m looking for an interesting way to reduce all repetition, but keep the legibility.  I&#8217;m thinking some nested context managers or decorators might be the way to go.  I like to hear ideas from other people, so, please, let me hear them.</p>
<p>By the way, all this code is from the command-line module of pitz, available <a href="http://github.com/mw44118/pitz/blob/master/pitz/cmdline.py">here</a>.  That&#8217;s where you can see all the different variations on the same theme.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2009/10/18/help-me-rewrite-some-repetitive-scripts/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Need help with data files and setup.py</title>
		<link>http://blog.tplus1.com/index.php/2009/04/21/need-help-with-data-files-and-setuppy/</link>
		<comments>http://blog.tplus1.com/index.php/2009/04/21/need-help-with-data-files-and-setuppy/#comments</comments>
		<pubDate>Wed, 22 Apr 2009 03:00:28 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[TurboGears]]></category>
		<category><![CDATA[mlp]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=337</guid>
		<description><![CDATA[I&#8217;m working on a package that includes some files that are meant to be copied and edited by people using the package.
My project is named &#8220;pitz&#8221; and it is a bugtracker.  Instead of using a config file to set the options for a project, I want to use python files.
When somebody installs pitz, I [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working on a package that includes some files that are meant to be copied and edited by people using the package.</p>
<p>My project is named &#8220;pitz&#8221; and it is a bugtracker.  Instead of using a config file to set the options for a project, I want to use python files.</p>
<p>When somebody installs pitz, I want to save some .py files somewhere so that when they run my pitz-setup script, I can go find those .py files and copy them into their working directory.</p>
<p>I have two questions:</p>
<ol>
<li>Do I need to write my setup.py file to specify that the .py files in particular directory need to be treated like data, not code?  For example, I don&#8217;t want the installer to hide those files inside an egg.</li>
<li>How can I find those .py files later and copy them?</li>
</ol>
<p>Here&#8217;s my setup.py so far:<br />
<pre><code>
from setuptools import setup, find_packages
version = &#039;0.1&#039;
setup(name=&#039;pitz&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;version=version,
&nbsp;&nbsp;&nbsp;&nbsp;description=&quot;Python to-do tracker inspired by ditz (ditz.rubyforge.org)&quot;,

&nbsp;&nbsp;&nbsp;&nbsp;long_description=&quot;&quot;&quot;
ditz (http://ditz.rubyforge.org) is the best distributed ticketing
system that I know of.&nbsp;&nbsp;There&#039;s a few things I want to change, so I
started pitz.&quot;&quot;&quot;,

&nbsp;&nbsp;&nbsp;&nbsp;classifiers=[],
&nbsp;&nbsp;&nbsp;&nbsp;keywords=&#039;ditz&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;author=&#039;Matt Wilson&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;author_email=&#039;matt@tplus1.com&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;url=&#039;http://tplus1.com&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;license=&#039;&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;packages=find_packages(exclude=[&#039;ez_setup&#039;, &#039;examples&#039;, &#039;tests&#039;]),

&nbsp;&nbsp;&nbsp;&nbsp;include_package_data=True,
&nbsp;&nbsp;&nbsp;&nbsp;package_dir={&#039;pitz&#039;:&#039;pitz&#039;},

&nbsp;&nbsp;&nbsp;&nbsp;data_files=[(&#039;share/pitz&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;pitz/pitztypes/agilepitz.py.sample&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;pitz/pitztypes/tracpitz.py.sample&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;])],

&nbsp;&nbsp;&nbsp;&nbsp;zip_safe=False,
&nbsp;&nbsp;&nbsp;&nbsp;install_requires=[
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# &#039;PyYAML&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# &#039;sphinx&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# &#039;nose&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# &#039;jinja2&#039;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# -*- Extra requirements: -*-
&nbsp;&nbsp;&nbsp;&nbsp;],

&nbsp;&nbsp;&nbsp;&nbsp;# I know about the much fancier entry points, but I prefer this
&nbsp;&nbsp;&nbsp;&nbsp;# solution.&nbsp;&nbsp;Why does everything have to be zany?
&nbsp;&nbsp;&nbsp;&nbsp;scripts = [&#039;scripts/pitz-shell&#039;],

&nbsp;&nbsp;&nbsp;&nbsp;test_suite = &#039;nose.collector&#039;,
)
</code></pre></p>
<p>When I run python setup.py install, I do get those .sample files copied, but they get copied into a folder way inside of my pitz install:<br />
<pre><code>
$ cd ~/virtualenvs/scratch/lib/
$ find -type f -name &#039;*.sample&#039;
./python2.6/site-packages/pitz-0.1dev-py2.6.egg/share/pitz/tracpitz.py.sample
./python2.6/site-packages/pitz-0.1dev-py2.6.egg/share/pitz/agilepitz.py.sample
</code></pre></p>
<p>I don&#8217;t know how I can write a script to copy those tracpitz.py.sample files out.  Maybe I can ask pitz what its version is, and then build a tring and use os.path.join, but that doesn&#8217;t look like any fun at all.</p>
<p>So, what should I do instead?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2009/04/21/need-help-with-data-files-and-setuppy/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>How to see the to-do list for pitz</title>
		<link>http://blog.tplus1.com/index.php/2009/04/13/how-to-see-the-to-do-list-for-pitz/</link>
		<comments>http://blog.tplus1.com/index.php/2009/04/13/how-to-see-the-to-do-list-for-pitz/#comments</comments>
		<pubDate>Tue, 14 Apr 2009 02:41:12 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=319</guid>
		<description><![CDATA[pitz is (among other things) a to-do list tracker like trac or bugzilla or version one.
I&#8217;m storing the list of stuff to do for pitz in the pitz source code.  Here&#8217;s how to see the unfinished stuff in pitz.
Get a copy of the code

$ git clone git://github.com/mw44118/pitz.git
Initialized empty Git repository in /home/matt/pitz/.git/
remote: Counting objects: [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://pitz.tplus1.com">pitz</a> is (among other things) a to-do list tracker like trac or bugzilla or version one.</p>
<p>I&#8217;m storing the list of stuff to do for pitz in the pitz source code.  Here&#8217;s how to see the unfinished stuff in pitz.</p>
<h2>Get a copy of the code</h2>
<p><pre><code>
$ git clone git://github.com/mw44118/pitz.git
Initialized empty Git repository in /home/matt/pitz/.git/
remote: Counting objects: 621, done.
remote: Compressing objects: 100% (604/604), done.
remote: Total 621 (delta 383), reused 0 (delta 0)
Receiving objects: 100% (621/621), 98.52 KiB | 135 KiB/s, done.
Resolving deltas: 100% (383/383), done.
</code></pre></p>
<h2>Install it, probably in a virtualenv</h2>
<p><pre><code>
$ source ~/virtualenvs/pitz/bin/activate
$ python setup.py develop
</code></pre></p>
<h2>Fire up pitz-shell</h2>
<p>I have written one command-line tool so far: <b>pitz-shell</b>.  Use it to start a python interpreter loaded with any pitz project.  Here&#8217;s how to start a session for pitz itself:<br />
<pre><code>
$ pitz-shell pitz/pitzfiles/project-99c58812-5c1c-4fec-874c-c998933ba88b.yaml
/home/matt/virtualenvs/pitz/lib/python2.6/site-packages/ipython-0.9.1-py2.6.egg/IPython/Magic.py:38: DeprecationWarning: the sets module is deprecated
&nbsp;&nbsp;from sets import Set
</code></pre></p>
<p>pitz-shell imports a bunch of classes and makes an object named p (p stands for project).  p has all the information about the project described in the yaml file passed in as an argument to pitz-shell.  The __repr__ method on p gives some summarized data:<br />
<pre><code>
In [1]: p
Out[1]: &lt;pitz.PitzProject &#039;Pitz&#039; (31 task entities, 1 person entities, 1 milestone entities) sorted by by_whatever([&#039;type&#039;, &#039;status&#039;, &#039;created time&#039;])&gt;
</code></pre><br />
p.todo is a property that just returns a bag of unfinished tasks for the project:<br />
<pre><code>
In [4]: p.todo

Out[4]: &lt;pitz.Bag &#039;Stuff to do&#039; (23 task entities) sorted by by_whatever([&#039;type&#039;, &#039;status&#039;, &#039;created time&#039;])&gt;
</code></pre><br />
You can print any bag to see all the contents of the bag, and p.todo is no different:<br />
<pre><code>
In [5]: print(p.todo)
===========
Stuff to do
===========

(23 task entities)
------------------

&nbsp;&nbsp; 0: Add support for something like &#039;ditz grep&#039; (unknown status)
&nbsp;&nbsp; 1: Update entities by loading a CSV file (unknown status)
&nbsp;&nbsp; 2: Figure out why some tasks are not converting pointers to objects (unknown status)
&nbsp;&nbsp; 3: Support intersection, union, and other set operations on bags (unknown status)
&nbsp;&nbsp; 4: Demonstrate really simple tasks and priorities workflow (unknown status)
&nbsp;&nbsp; 5: Support a .pitz config file with all pitz scripts (unknown status)
&nbsp;&nbsp; 6: Add a todo property on project (or maybe bag) (unknown status)
&nbsp;&nbsp; 7: Write code to use strings as keys (unknown status)
&nbsp;&nbsp; 8: Prompt to save work at the end of an interactive pitz session (unknown status)
&nbsp;&nbsp; 9: Make it possible to support a filter like attribute!=value (unknown status)
&nbsp;&nbsp;10: Write code to support sorting by anything (unknown status)
&nbsp;&nbsp;11: Support hooks (unknown status)
&nbsp;&nbsp;12: Write an attributes property on a bag that lists count of each attribute in any entities (unknown status)
&nbsp;&nbsp;13: Allow two bags to be compared for equality by using their entities (unknown status)
&nbsp;&nbsp;14: Make it easy to list each employee&#039;s tasks (unknown status)
&nbsp;&nbsp;15: Support a $PITZDIR env var to tell where yaml files live (unknown status)
&nbsp;&nbsp;16: Demonstrate release -&lt; iteration -&lt; user story -&lt; task workflow. (unknown status)
&nbsp;&nbsp;17: Load new entities from a CSV file (unknown status)
&nbsp;&nbsp;18: Support grep on entities (unknown status)
&nbsp;&nbsp;19: write data to yaml in order (unknown status)
&nbsp;&nbsp;20: Support entity subclasses like releases, iterations, user stories, and tasks (unknown status)
&nbsp;&nbsp;21: A bag should dump to a single CSV file (unknown status)
&nbsp;&nbsp;22: Support using substring of name as name (unknown status)
</code></pre></p>
<p>That&#8217;s how you see the to-do list for pitz!</p>
<p>In a future post, I&#8217;ll show how to make new tasks and how to update tasks.</p>
<p>I also need to explain how Pitz lets you come up with whatever wacky workflow you want.  When you set up a pitz project, you can use the classes I came up with, or subclass Entity into your own weird types.  In a future post, I&#8217;ll show I&#8217;m using pitz to model an agile development system using releases, iterations, checkpoints, user stories, tasks, and people.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2009/04/13/how-to-see-the-to-do-list-for-pitz/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>pitz data model outline</title>
		<link>http://blog.tplus1.com/index.php/2009/04/04/pitz-data-model-outline/</link>
		<comments>http://blog.tplus1.com/index.php/2009/04/04/pitz-data-model-outline/#comments</comments>
		<pubDate>Sat, 04 Apr 2009 21:13:57 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=313</guid>
		<description><![CDATA[I just finished a whole bunch of documentation on the pitz data model. You can read it here or just read all the stuff I copied below:
There are two classes in pitz: entities and bags. Everything else are subclasses of these two.
Entities
Making them
Every entity is an object like a dictionary. You can make an entity [...]]]></description>
			<content:encoded><![CDATA[<p>I just finished a whole bunch of documentation on the pitz data model. You can read it <a href="http://pitz.tplus1.com/data-model.html">here</a> or just read all the stuff I copied below:</p>
<p>There are two classes in pitz: entities and bags. Everything else are subclasses of these two.</p>
<h2>Entities</h2>
<h3>Making them</h3>
<p>Every entity is an object like a dictionary. You can make an entity like this:</p>
<p><pre><code>
&gt;&gt;&gt; from pitz import Entity
&gt;&gt;&gt; e = Entity(title=&quot;example entity&quot;,
...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;creator=&quot;Matt&quot;,
...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;importance=&quot;not very&quot;)
</code></pre></p>
<p>You can also load an entity from a yaml file, but I’ll explain that later.</p>
<p>You can look up a value for any attribute like this:</p>
<p><pre><code>
&gt;&gt;&gt; e[&#039;title&#039;]
&#039;example entity&#039;
&gt;&gt;&gt; e.keys() #doctest: +NORMALIZE_WHITESPACE
[&#039;name&#039;, &#039;creator&#039;, &#039;importance&#039;, &#039;title&#039;, &#039;modified_time&#039;,
&#039;created_time&#039;, &#039;type&#039;]
&gt;&gt;&gt; e[&#039;type&#039;]
&#039;entity&#039;
</code></pre></p>
<h3>Viewing them</h3>
<p>Entities have a summarized view useful when you want to see a list of entities, and a detailed view that shows all the boring detail:<br />
<pre><code>
&gt;&gt;&gt; e.summarized_view
&#039;example entity (entity)&#039;

&gt;&gt;&gt; print(e.detailed_view) #doctest: +SKIP
example entity (entity)
-----------------------

name:
entity-bdd31951-cff0-42a5-92b4-97ef966a6f6f

creator:
Matt

importance:
not very

title:
example entity

modified_time:
2009-04-04 07:47:09.456068

created_time:
2009-04-04 07:47:09.456068

type:
entity
</code></pre><br />
Notice how our entity has some attributes we never set, like name, type, created_time, and modified_time. I make these in the __init__ method of the entity class.</p>
<p>By the way, you can ignore the #doctest: +SKIP comment. That is there so the doctests will skip trying to running this example, which will generate unpredictable values.</p>
<h3>Saving and loading them</h3>
<p>Entities have an instance method named to_yaml_file and a from_yaml_file classmethod. Here’s how to use them:<br />
<pre><code>
&gt;&gt;&gt; outfile = e.to_yaml_file(&#039;.&#039;) # Writes file to this directory.
&gt;&gt;&gt; e2 = Entity.from_yaml_file(outfile)
</code></pre></p>
<h2>Bags</h2>
<h3>Making them</h3>
<p>While entities are based on dictionaries, bags are based on lists. You can give a bag instance a title, which is nice for remembering what it is you want it for. Bags make it easy to organize a bunch of entities.<br />
<pre><code>
&gt;&gt;&gt; from pitz import Bag
&gt;&gt;&gt; b = Bag(title=&quot;Stuff that is not very important&quot;)
&gt;&gt;&gt; b.append(e)
</code></pre></p>
<h3>Viewing them</h3>
<p>Converting a bag to a string prints the summarized view of all the entities inside:<br />
<pre><code>
&gt;&gt;&gt; print(b) #doctest: +SKIP
================================
Stuff that is not very important
================================

1 entity entities
-----------------

&nbsp;&nbsp; 0: example entity (entity)
</code></pre><br />
That number 0 can be used to pull out the entity at that position, just like a regular boring old list:<br />
<pre><code>
&gt;&gt;&gt; e == b[0]
True
</code></pre></p>
<h3>Querying them</h3>
<p>Bags have a matches_dict method that accepts a bunch of key-value pairs and then returns a new bag that contains all the entities in the first bag that match all those key-value pairs.</p>
<p>First, I’ll make a few more entities:<br />
<pre><code>
&gt;&gt;&gt; e1 = Entity(title=&quot;example #1&quot;, creator=&quot;Matt&quot;,
...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; importance=&quot;Really important&quot;)
&gt;&gt;&gt; e2 = Entity(title=&quot;example #2&quot;, creator=&quot;Matt&quot;,
...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; importance=&quot;not very&quot;)
</code></pre><br />
Now I’ll make a new bag that has both of these new entities:<br />
<pre><code>
&gt;&gt;&gt; b = Bag(&#039;Everything&#039;)
&gt;&gt;&gt; b.append(e1)
&gt;&gt;&gt; b.append(e2)
&gt;&gt;&gt; print(b)
==========
Everything
==========
&lt;BLANKLINE&gt;
2 entity entities
-----------------
&lt;BLANKLINE&gt;
&nbsp;&nbsp; 0: example #1 (entity)
&nbsp;&nbsp; 1: example #2 (entity)
&lt;BLANKLINE&gt;
</code></pre><br />
Here is how to get a new bag with just the entities that have an importance attribute set to “not very”:<br />
<pre><code>
&gt;&gt;&gt; not_very_important = b.matches_dict(importance=&quot;not very&quot;)
&gt;&gt;&gt; len(not_very_important) == 1
True
&gt;&gt;&gt; not_very_important[0] == e2
True
</code></pre><br />
Since matches_dict is the most common method I call on a bag, I made the __call__ method on the Bag class run matches_dict. So that means this works just as well:<br />
<pre><code>
&gt;&gt;&gt; not_very_important = b(importance=&quot;not very&quot;)
</code></pre></p>
<h3>Saving and loading them</h3>
<p>Bags can send all contained entities to yaml files with to_yaml_files, and bags can load a bunch of entities from yaml files with from_yaml_files.</p>
<p>Right now, there is no way for a bag to save itself to yaml.</p>
<h2>The Special Project Bag</h2>
<p>After I finished bags and entities, I thought I was done, but then I ran into a few frustrations:</p>
<ul>
<li>When I made a bunch of entities, but didn’t append them all into one bag, then I couldn’t run filters across all of them.</li>
<li>At the end of a session, it wasn’t easy for me to make sure that all of the entities got saved out to yaml.</li>
<li>I couldn’t figure out an elegant way to store one entity as a value for another entity’s attribute.</li>
</ul>
<p>So I made a “special” Bag subclass called Project. The idea here is that every entity should be a member of the project bag. Also, every entity should have a reference back to the project.</p>
<p>Using a project is easy. Just pass it in as the first argument when you make an entity. Imagine I want to link some tasks to Matt and some other tasks to Lindsey. First I make a project:<br />
<pre><code>
&gt;&gt;&gt; from pitz import Project
&gt;&gt;&gt; weekend_chores = Project(title=&quot;Weekend chores&quot;)
</code></pre><br />
Now I make the rest of the entities:<br />
<pre><code>
&gt;&gt;&gt; matt = Entity(weekend_chores, title=&quot;Matt&quot;)
&gt;&gt;&gt; lindsey = Entity(weekend_chores, title=&quot;Lindsey&quot;)
&gt;&gt;&gt; t1 = Entity(weekend_chores, title=&quot;Mow the yard&quot;, assigned_to=matt)
&gt;&gt;&gt; t2 = Entity(weekend_chores, title=&quot;Buy some groceries&quot;,
...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assigned_to=lindsey)
</code></pre><br />
Now it is easy to get tasks for matt:<br />
<pre><code>
&gt;&gt;&gt; chores_for_matt = weekend_chores(assigned_to=matt)
&gt;&gt;&gt; mow_the_yard = chores_for_matt[0]
&gt;&gt;&gt; mow_the_yard[&#039;assigned_to&#039;] == matt
True
</code></pre></p>
<h2>Pointers</h2>
<p>There’s a problem in that last example: when I send this mow_the_yard entity out to a YAML file, what will I store as the value for the “assigned_to” attribute?</p>
<p>In SQL, this is what foreign keys are good for. In my chores table, I would store a reference to a particular row in the people table.</p>
<p>I wanted the same functionality in pitz, so I came up with pointers.  This is dry stuff, so here’s an example:<br />
<pre><code>
&gt;&gt;&gt; class Chore(Entity):
...&nbsp;&nbsp;&nbsp;&nbsp; pointers = dict(assigned_to=&#039;person&#039;)
...
&gt;&gt;&gt; class Person(Entity):
...&nbsp;&nbsp;&nbsp;&nbsp; pass
&gt;&gt;&gt; matt = Person(weekend_chores, title=&quot;Matt&quot;)
&gt;&gt;&gt; lindsey = Person(weekend_chores, title=&quot;Lindsey&quot;)
&gt;&gt;&gt; ch1 = Chore(weekend_chores, title=&quot;Mow the yard&quot;, assigned_to=matt)
&gt;&gt;&gt; ch2 = Chore(weekend_chores, title=&quot;Buy some groceries&quot;,
...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assigned_to=lindsey)
</code></pre><br />
Not much is different, but instead of matt, lindsey, and the various chores all being entities, they’re now subclasses. But here’s the advantage of defining pointers on Chore:<br />
<pre><code>
&gt;&gt;&gt; ch1[&#039;assigned_to&#039;]
&lt;pitz.Person &#039;Matt&#039;&gt;
&gt;&gt;&gt; matt[&#039;name&#039;] # doctest: +SKIP
&#039;person-530ad3cc-14f1-491a-bdb6-ed1dd65afe46&#039;
&gt;&gt;&gt; ch1.replace_objects_with_pointers()
&gt;&gt;&gt; ch1[&#039;assigned_to&#039;] # doctest: +SKIP
&#039;person-530ad3cc-14f1-491a-bdb6-ed1dd65afe46&#039;
</code></pre><br />
First of all, notice how I printed out the name attribute on matt.</p>
<p>After running the replace_objects_with_pointers method, I don’t have a reference to the matt object. Instead, I have matt’s name now.</p>
<p>Now I can send this data out to a yaml file. And when I load it back in from yaml, I can then reverse this action, and go look up an entity with the same name:<br />
<pre><code>
&gt;&gt;&gt; mn = matt.name
&gt;&gt;&gt; matt == weekend_chores.by_name(mn)
True
</code></pre><br />
In practice, I convert all the entities to pointers, then write out the yaml files, then convert all the pointers back into objects automatically.</p>
<p>That&#8217;s the end of the data model documentation.  I hope that shines enough light so that it is obvious if pitz would be useful to you or not.</p>
<p>I&#8217;m working on a separate article where I show some real-world workflows modeled in pitz, but that will be next week&#8217;s post.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2009/04/04/pitz-data-model-outline/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>How to load ditz issues into python</title>
		<link>http://blog.tplus1.com/index.php/2009/03/04/how-to-load-ditz-issues-into-python/</link>
		<comments>http://blog.tplus1.com/index.php/2009/03/04/how-to-load-ditz-issues-into-python/#comments</comments>
		<pubDate>Wed, 04 Mar 2009 15:57:32 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=301</guid>
		<description><![CDATA[Ditz is a fantastic distributed bug tracking system written in Ruby.
Here&#8217;s some code that you can use to load some ditz issues into a python interpreter.  You need to install my pitz project first though.

&#62;&#62;&#62; from pitz.junkyard.ditzloader import *
&#62;&#62;&#62; from glob import glob
&#62;&#62;&#62; issue_file_path = glob(&#039;../.ditz/issue-*.yaml&#039;)[0]
&#62;&#62;&#62; import yaml
&#62;&#62;&#62; issue = yaml.load(open(issue_file_path))
&#62;&#62;&#62; issue.title
&#039;Distribute reports by [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://ditz.rubyforge.org">Ditz</a> is a fantastic distributed bug tracking system written in Ruby.</p>
<p>Here&#8217;s some code that you can use to load some ditz issues into a python interpreter.  You need to install my <a href="http://pitz.tplus1.com/">pitz</a> project first though.</p>
<p><pre><code>
&gt;&gt;&gt; from pitz.junkyard.ditzloader import *
&gt;&gt;&gt; from glob import glob
&gt;&gt;&gt; issue_file_path = glob(&#039;../.ditz/issue-*.yaml&#039;)[0]
&gt;&gt;&gt; import yaml
&gt;&gt;&gt; issue = yaml.load(open(issue_file_path))
&gt;&gt;&gt; issue.title
&#039;Distribute reports by email&#039;
&gt;&gt;&gt; print issue.desc
Somehow allow people to sign up for report subscriptions.
</code></pre><br />
<pre><code>
So when new reports come out, they get updated.&nbsp;&nbsp;Maybe I can use
&lt;b&gt;RSS&lt;/b&gt; feeds to hold the reports.
&gt;&gt;&gt; print issue.log_events
[[datetime.datetime(2008, 9, 2, 17, 47, 44, 549355), &#039;Matthew Wilson &lt;mw@staffknex.com&gt;&#039;, &#039;created&#039;, &#039;&#039;], [datetime.datetime(2008, 9, 2, 19, 18, 3, 286902), &#039;Matthew Wilson &lt;mw@staffknex.com&gt;&#039;, &#039;assigned to release 3.5.1 from unassigned&#039;, &#039;&#039;], [datetime.datetime(2008, 9, 4, 18, 27, 19, 571991), &#039;Matthew Wilson &lt;mw@staffknex.com&gt;&#039;, &#039;unassigned from release 3.5.1&#039;, &#039;&#039;]]
</code></pre></p>
<p>That above just showed how to read the ditz issue.  I&#8217;m having trouble updating the issue and then saving it in a format that ditz can still read.  Updating the ditz issue and saving it out again is easy:</p>
<p><pre><code>
&gt;&gt;&gt; issue.title = &#039;Distribute reports by email or RSS&#039;
&gt;&gt;&gt; open(issue_file_path, &#039;w&#039;).write(yaml.dump(issue))
</code></pre></p>
<p>But when I try to load it with ditz, I get this error:<br />
<pre><code>
$ ditz show 1209b
/home/matt/checkouts/ditz/lib/ditz/model-objects.rb:124:in `sort_by&#039;: comparison of String with Time failed (ArgumentError)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /home/matt/checkouts/ditz/lib/ditz/model-objects.rb:124:in `assign_issue_names!&#039;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /home/matt/checkouts/ditz/lib/ditz/model-objects.rb:51:in `issues=&#039;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /home/matt/checkouts/ditz/lib/ditz/file-storage.rb:21:in `load&#039;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from /home/matt/checkouts/ditz/bin/ditz:165
</code></pre></p>
<p>I compared my dumped file to the original ruby file and found that python wrote dates out like this:<br />
<pre><code>
&gt;&gt;&gt; print yaml.dump(issue.creation_time)
2008-09-02 17:47:43.268059
</code></pre></p>
<p>But in the ruby yaml files, the dates look like this:<br />
<pre><code>
$ grep creation_time ../../../.ditz/issue-1209b17b64335383a710ccadf10b74c3401dbcb2.yaml 
creation_time: 2008-09-02 17:47:43.268059 Z
</code></pre></p>
<p>That trailing Z seems important.</p>
<p>Also, python and ruby seem to write out lists differently.  Here&#8217;s how python dumped a list of lists:<br />
<pre><code>
&gt;&gt;&gt; print yaml.dump(issue.log_events)
- [!!timestamp &#039;2008-09-02 17:47:44.549355&#039;, Matthew Wilson &lt;mw@staffknex.com&gt;, created,
&nbsp;&nbsp;&#039;&#039;]
- [!!timestamp &#039;2008-09-02 19:18:03.286902&#039;, Matthew Wilson &lt;mw@staffknex.com&gt;, assigned
&nbsp;&nbsp;&nbsp;&nbsp;to release 3.5.1 from unassigned, &#039;&#039;]
- [!!timestamp &#039;2008-09-04 18:27:19.571991&#039;, Matthew Wilson &lt;mw@staffknex.com&gt;, unassigned
&nbsp;&nbsp;&nbsp;&nbsp;from release 3.5.1, &#039;&#039;]
</code></pre></p>
<p>But the same data dumped by ruby looks like:<br />
<pre><code>
log_events:
- - 2008-09-02 17:47:44.549355 Z
&nbsp;&nbsp;- Matthew Wilson &lt;mw@staffknex.com&gt;
&nbsp;&nbsp;- created
&nbsp;&nbsp;- &quot;&quot;
- - 2008-09-02 19:18:03.286902 Z
&nbsp;&nbsp;- Matthew Wilson &lt;mw@staffknex.com&gt;
&nbsp;&nbsp;- assigned to release 3.5.1 from unassigned
&nbsp;&nbsp;- &quot;&quot;
- - 2008-09-04 18:27:19.571991 Z
&nbsp;&nbsp;- Matthew Wilson &lt;mw@staffknex.com&gt;
&nbsp;&nbsp;- unassigned from release 3.5.1
&nbsp;&nbsp;- &quot;&quot;
</code></pre></p>
<p>So, there&#8217;s clearly some more work for me (or you, this is an open-source project) to do.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2009/03/04/how-to-load-ditz-issues-into-python/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Some progress on pitz</title>
		<link>http://blog.tplus1.com/index.php/2009/03/01/some-progress-on-pitz/</link>
		<comments>http://blog.tplus1.com/index.php/2009/03/01/some-progress-on-pitz/#comments</comments>
		<pubDate>Mon, 02 Mar 2009 03:58:54 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/?p=290</guid>
		<description><![CDATA[Pitz is my open-source, distributed, plain-text, command-line, very flexible issue tracker.  I just started working on it and I&#8217;m looking for feedback and contributors.
This post describes a little of what I&#8217;ve gotten done so far.  I haven&#8217;t written any interface yet other than from within an interactive python session.  Deal with it.
Getting [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://pitz.tplus1.com">Pitz</a> is my open-source, distributed, plain-text, command-line, very flexible issue tracker.  I just started working on it and I&#8217;m looking for feedback and contributors.</p>
<p>This post describes a little of what I&#8217;ve gotten done so far.  I haven&#8217;t written any interface yet other than from within an interactive python session.  Deal with it.</p>
<h2>Getting started</h2>
<p>You have to make a bag to keep all the tasks and then you make tasks with any keywords you can dream up.</p>
<p><pre><code>
&gt;&gt;&gt; from pitz import Bag, Task
&gt;&gt;&gt; b = Bag(&#039;.pitz&#039;)
&gt;&gt;&gt; b.append(Task(title=&#039;Wash the dishes&#039;, creator=&#039;Matt&#039;, importance=&#039;Not very&#039;))
&gt;&gt;&gt; b.append(Task(title=&#039;Clean the cat box&#039;, creator=&#039;Matt&#039;, importance=&#039;Not very&#039;))
</code></pre></p>
<p>You can add tasks to the bag by using the append method on the bag, or by passing in the bag as the first argument to the task:<br />
<pre><code>
&gt;&gt;&gt; Task(b, title=&#039;Set new high score on Sushi-go-round&#039;, creator=&#039;Matt&#039;, importance=&#039;critical&#039;)
</code></pre></p>
<p>Printing a bag really prints the title of each task in the bag:</p>
<p><pre><code>
&gt;&gt;&gt; print(b)
Clean the cat box
Set new high score on Sushi-go-round
Wash the dishes
</code></pre></p>
<h2>Running queries</h2>
<p>I&#8217;m proud of this one.  This is the main reason I&#8217;m working on ditz.  Bag instances have a feature called matching_pairs that can filter the tasks down to a smaller new bag.</p>
<p>You can filter by a single pair like this:</p>
<p><pre><code>
&gt;&gt;&gt; critical_tasks = b.matching_pairs([(&#039;importance&#039;, &#039;critical&#039;)])
&gt;&gt;&gt; print(critical_tasks)
Set new high score on Sushi-go-round
</code></pre></p>
<p>Or you can filter by multiple pairs and the filtered tasks must satisfy ALL the pairs.<br />
<pre><code>
&gt;&gt;&gt; print(b.matching_pairs([(&#039;creator&#039;, &#039;Matt&#039;), (&#039;importance&#039;, &#039;Not very&#039;)]))
Clean the cat box
Wash the dishes
</code></pre></p>
<h2>Dumping and loading</h2>
<p>Use the bag that holds all the tasks to quickly write all the tasks out to your hard drive like this:</p>
<p><pre><code>
&gt;&gt;&gt; b.to_yaml_files()
[&#039;.pitz/task-07e1af97-0ac6-4904-9187-0c2fd61692b6.yaml&#039;, &#039;.pitz/task-6a7af07c-d0fb-4a77-9347-8dc78ef490fe.yaml&#039;, &#039;.pitz/task-5ce725dc-c1db-4eca-a74c-55cd0e910786.yaml&#039;]
</code></pre></p>
<p>The returned stuff is a list of files that pitz just wrote.</p>
<p>Loading from the hard drive is pretty simple too.  Just tell the bag where to load from.<br />
<pre><code>
&gt;&gt;&gt; b2 = Bag(&#039;.pitz&#039;)
&gt;&gt;&gt; print(b2)
Clean the cat box
Set new high score on Sushi-go-round
Wash the dishes
</code></pre></p>
<h2>Task details</h2>
<p>Printing a task by itself gives all the details on the task.</p>
<p><pre><code>
&gt;&gt;&gt; t = Task.from_yaml_file (&#039;.pitz/task-4d9c1db2-fef3-4b50-8095-b2339384e118.yaml&#039;)
&gt;&gt;&gt; print(t)
Do the 2008 taxes
-----------------

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type: Task
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: task-4d9c1db2-fef3-4b50-8095-b2339384e118
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; title: Do the 2008 taxes
&nbsp;&nbsp;&nbsp;&nbsp;created date: 2009-03-01 22:29:58.242512
&nbsp;&nbsp; modified date: 2009-03-01 22:29:58.242512
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; creator: Matt
last modified by: Matt

&nbsp;&nbsp;&nbsp;&nbsp; description: 
Do the 2008 taxes
</code></pre></p>
<h2>Other stuff</h2>
<p>You can use bags as iterators to go through the tasks one-by-one:<br />
<pre><code>
for task in b:
&nbsp;&nbsp;&nbsp;&nbsp;print(t[&#039;title&#039;])
</code></pre></p>
<p>Also notice that tasks are really just subclassed dictionaries (UserDict, actually) with some extra methods bolted on.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2009/03/01/some-progress-on-pitz/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>My new ticket tracking system is now vaporware!</title>
		<link>http://blog.tplus1.com/index.php/2009/02/22/my-new-ticket-tracking-system-is-now-vaporware/</link>
		<comments>http://blog.tplus1.com/index.php/2009/02/22/my-new-ticket-tracking-system-is-now-vaporware/#comments</comments>
		<pubDate>Mon, 23 Feb 2009 02:55:24 +0000</pubDate>
		<dc:creator>matt</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[pitz]]></category>

		<guid isPermaLink="false">http://blog.tplus1.com/index.php/2009/02/22/my-new-ticket-tracking-system-is-now-vaporware/</guid>
		<description><![CDATA[I set up http://pitz.tplus1.com to host my pitz project, which is a python implementation of ditz.
Instead of just banging out code, I decided to write the documentation and the list of supported features FIRST.
Once I have my feature set established, then I&#8217;ll write the tests for those features, and finally, I&#8217;ll write the code.
I&#8217;m looking [...]]]></description>
			<content:encoded><![CDATA[<p>I set up <a href="http://pitz.tplus1.com">http://pitz.tplus1.com</a> to host my pitz project, which is a python implementation of <a href="http://ditz.rubyforge.org">ditz</a>.</p>
<p>Instead of just banging out code, I decided to write the documentation and the list of supported features FIRST.</p>
<p>Once I have my feature set established, then I&#8217;ll write the tests for those features, and finally, I&#8217;ll write the code.</p>
<p>I&#8217;m looking for feedback on the feature set.  What would an ideal bugtracker look like?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.tplus1.com/index.php/2009/02/22/my-new-ticket-tracking-system-is-now-vaporware/feed/</wfw:commentRss>
		<slash:comments>29</slash:comments>
		</item>
	</channel>
</rss>

