AstronomyPictureOfTheDay 2.0

There is a new release of AstronomyPictureOfTheDay available this
morning. Version 2.0 is a substantial rewrite of the 1.1 version, but
retains essentially the same functionality. The primary difference is
that you no longer have to run a script to “personalize” it during
installation. Now that it is distributed as an Application instead of an
Automator workflow, you can just drag it to your Applications folder.

The source is still created with Automator, but some of the Finder
actions for working with directories have been replaced by Shell Script
actions to perform the same operations. The Finder actions are
hard-coded to specific folder names, selected in the Automator UI when
the action is configured. With a Shell Script action, I can use
environment variables such as “$HOME” to make the action more flexible.
Using variables also avoids the problem of having everything in the
workflow tied to my own home directory, so that the paths needed to be
modified before the workflow was usable by anyone else.

I could, of course, have written the entire program as a shell script,
Python program, or whatever. But the point of building it with
Automator in the first place was that it was easy. I suppose I am
stretching the boundaries of where Automator is the easiest tool for
this particular job, but at least now the hack is hidden inside the
app instead of hanging out where everyone can see it in the
installation instructions.

Converting podcasts to regular tracks in iTunes

I have spent the better part of the morning trying to work out how to
convert podcasts to “regular” tracks in iTunes, so they would show up in
shuffle, etc. Mostly this was for my collection of Jonathan Coulton
Thing a Week” episodes, but it would be useful for anything you
wanted to move out of your podcast list into the main audio portion of
the library. I suppose the reason it took so long to find the solution
is I started by searching for it on Google instead of just looking
through the iTunes menu options, though as you will see the solution
wasn’t immediately obvious even once I had found it.

I knew that iTunes would let me change settings like “Remember
playback position” and “Skip when shuffling” from the info dialog
(Cmd-I), so I started there. The only setting that even mentioned
podcast was the genre, and I already knew that was not what I needed to
change. Some of the tracks already had “real” genres and only some were
set to “Podcast”.

I also knew there is a separate podcast flag available for queries in
Smart Lists and AppleScript, since I used it to create my “Active Queue”
podcast playlist with selections of episodes from various podcast series
(it’s like creating your own mix tape, but for talk radio). I tried to
write a simple AppleScript to change the podcast flag of selected tracks
to true. It turns out the flag is a read-only attribute. No amount of
searching uncovered any way to change the setting using AppleScript.

The first useful looking suggestions I ran into were to convert the
ID3 tag format to an older version
, then convert it back. Doing that
erased most of the comments and other meta-data associated with the
tracks, though, so I didn’t like the results.

Next I found a few forum and blog posts that talked about an
ITUNESPODCAST setting in the extended ID3 tags. They all mentioned a
Windows program for removing or changing the flag, though. I examined a
few of the files with Ned Batchelder’s python module id3reader, but
didn’t see anything that looked like “ITUNESPODCAST” in the output.

Going back to Google, I finally found a reference to converting the
files to AAC using an option in the Advanced menu. That seemed like
overkill, but at this point I was becoming fed up and just wanted to be
done with the whole thing. I could always re-encode as MP3, after all.
Well, iTunes didn’t have a menu option to “Encode as AAC”. It did have
“Convert selection to MP3”, which didn’t make much sense to me. As far
as I knew, the tracks were already MP3 files. But lo and behold,
selecting that menu option did enable them in the iTunes Music Library.
It made copies of all of the tracks as it converted them, so I could
even delete the podcast subscription.

So, if you want to add podcast episodes you have already downloaded to
your music library and turn off the podcast flag, select the track and
choose Advanced->Convert selection to MP3.

DjangoKit help?

I spent a little time last night trying to assemble an application
using DjangoKit without much success.

I’m running Python 2.5 on a PowerBook with Mac OS 10.4. I downloaded
and installed PyObjC from source so it would compile (I thought) against
the right version of Python, then installed DjangoKit using
python setup.py install. Everything seemed to be working, and I was
able to build an application. But when I ran that app, it produced an
error about the version of the SQLite libraries being used (2 instead of
3) and missing libraries.

I gave up on Python 2.5, re-installed PyObjC and DjangoKit for 2.4 and
tried again. Same error.

Just for grins, I copied the app over to my wife’s laptop (she has a
MacBook Pro). The result was, of course, a new error about the platform.
No universal binaries? Really?

I’m sure there are options, or something, that I’m leaving out when I
build the app. This was mostly an experiment, and I was in a hurry, so I
gave up easily and just installed the django code I wanted on an
existing (Linux) web server and let her use that instead of messing with
a desktop application.

Has anyone else had more success building portable Python apps, esp.
with django, on Mac OS X?

Updated

Of course I knew better than to post in frustration when I posted this
originally. In my haste, I didn’t post sample code, the error message,
or much of the rest of the information I would have wanted if I was the
DjangoKit author trying to help someone out. Nonetheless, Tom did some
digging anyway and offered suggestions. Others did as well. Thanks! I
finally found time to follow up, and am coming closer to an answer.

Here are the full details

The application is very, very simple. The model just contains 2
classes for creating an index of a pile of Cook’s Illustrated magazine
we have laying around the house. There is no front-end, since the admin
views already provide the functionality she wanted. I thought I would be
cute and bundle it as a desktop app for Ms. PyMOTW, instead of setting
the app up on my web server. I have packaged the sample code and placed
it on my server.

I have included 2 separate setup.py files (setup.py and
djangokit_setup.py). I couldn’t package the source using the DjangoKit
version of setup:

$ python djangokit_setup.py sdist --force-manifest
Loading 'initial_data' fixtures...
No fixtures found.
running sdist
warning: sdist: missing required meta-data: name, url
warning: sdist: manifest template 'MANIFEST.in' does not exist (using default file list)
error: dist/Cook's Illustrated Index.app/Contents/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5/Frameworks/Python.framework/Versions/2.5: File name too long

I only seem to get the error if my dist directory includes the
application, too. Otherwise I get a minimal package with the name
‘UNKNOWN’. So, the tarball was packaged with a regular distutils
setup.py. That’s not a big problem, since it is easy to use separate
files.

When I ran python djangokit_setup.py py2app, the first time it
reported this error:

*** creating application bundle: Cook's Illustrated Index ***
error: can't copy 'media': doesn't exist or not a regular file

I eventually figured out (guessed) that even though I don’t have any
external media, I need a media directory at the same level in the
directory tree as the setup file. Creating the directory let me create
the app. Running that app gives me this traceback:

Traceback (most recent call last):
  File "/Users/dhellmann/Devel/personal/CooksIndex/trunk/dist/Cook's Illustrated Index.app/Contents/Resources/__boot__.py", line 31, in
    _run('app.py')
  File "/Users/dhellmann/Devel/personal/CooksIndex/trunk/dist/Cook's Illustrated Index.app/Contents/Resources/__boot__.py", line 28, in _run
    execfile(path, globals(), globals())
  File "/Users/dhellmann/Devel/personal/CooksIndex/trunk/dist/Cook's Illustrated Index.app/Contents/Resources/app.py", line 9, in
    from pysqlite2 import dbapi2 as sqlite
ImportError: No module named pysqlite2

That brings the error reporting up to date, without trying any of the
suggestions in the comments, yet. As I mentioned, the code itself works
if I run django outside of the packaged application (from the command
line, etc.). So I’m confident that my own imports are valid, etc.

Based on a hint from Tom (in the comments, he suggests that I install
pysqlite2), I tried editing the app.py file created inside the
application to import from sqlite3 instead of sqlite2. Editing the file
directly didn’t do it. Editing the copy already in my application
changed the error message to:

Traceback (most recent call last):
  File "/Users/dhellmann/Devel/personal/CooksIndex/trunk/dist/Cook's Illustrated Index.app/Contents/Resources/__boot__.py", line 31, in
    _run('app.py')
  File "/Users/dhellmann/Devel/personal/CooksIndex/trunk/dist/Cook's Illustrated Index.app/Contents/Resources/__boot__.py", line 28, in _run
    execfile(path, globals(), globals())
  File "/Users/dhellmann/Devel/personal/CooksIndex/trunk/dist/Cook's Illustrated Index.app/Contents/Resources/app.py", line 10, in
    from sqlite3 import dbapi2 as sqlite
ImportError: No module named sqlite3

Next I tried editing the version of app.py in
/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/djangokit.
After removing the application and rebuilding, I see the same error.

So I finally broke down and installed the pysqlte2 package Tom
pointed out for me. The package seems to imply that it is for Python
2.4, and I’m running 2.5, but I installed it anyway.

The application Packaged with python 2.5 gave me “No module named
pysqlite2” when I ran it. Repackaged using “python2.4
djangokit_setup.py py2app”, I got the app to run but it does not seem
to actually work. The console log shows this:

2007-06-23 15:07:28.403 Cook's Illustrated Index[14739] creating support folder /Users/dhellmann/Library/Application Support/DjangoKit/CooksIndex
2007-06-23 15:07:28.405 Cook's Illustrated Index[14739] installing default database
Starting web server on port 10557
Unhandled exception in thread started by
Traceback (most recent call last):
  File "/Users/dhellmann/Devel/personal/CooksIndex/trunk/dist/Cook's Illustrated Index.app/Contents/Resources/app.py", line 81, in startWebServer
    handler = AdminMediaHandler(WSGIHandler(), path)
TypeError: __init__() takes exactly 2 arguments (3 given)

So, I am a lot closer but not quite where I would like to be. I don’t
really care whether I package under 2.4 or 2.5, so long as the result
runs on my wife’s laptop, which doesn’t have any development packages
installed.

Dialing down email distractions

I have been experimenting with various productivity hacks lately. I
feel like I’m already fairly productive, based on tracking the amount of
work I accomplish week-to-week. So instead of trying to do more, I’m
trying to maintain the same level of output with less effort (and
hopefully time).

One of the top tips I have seen repeatedly is to reduce the amount of
time spent checking email, and only check it a couple of times per day.
I’m not sure I’m ready go go that hard-core, yet. Our team is spread out
and relies heavily on email for communication. Most of the messages are
important, and there aren’t too many that I feel like I have trouble
keeping up. The biggest issue is that they can be very time-sensitive.
Our code-review process is triggered via email messages generated when a
ticket is given a specific status in trac. If one of us does not notice
the change, the author of the change might be blocked for some period of
time from doing dependent work, until the code review can be completed.
Since we want to encourage code reviews, we don’t want those blockages
to last too long. So, I can’t turn email off entirely. But I can dial it
down fairly far.

Besides cutting down on email during the week, I also want to break
myself of the habit of checking work email over the weekend. Working for
a startup, it is too easy (for me) to get sucked into giving up weekend
after weekend. This is draining, so I’m not as fresh during the week as
I would be if I avoided the work mail. But, I don’t want to give up my
personal email at the same time.

image0So, the question is, how do I strike that balance?

The first step was to decide what my new mail schedule would be. I
tend to get up fairly early in the mornings, and enjoy breakfast on the
patio when the weather is nice enough (it rained last night, finally).
This is relaxing time, but not especially conducive to the long
stretches of deep thought needed for development or debugging. So,
mornings are a good time to do a lot of email, instant messaging with
the rest of the team, review code or documentation, and the other sorts
of tasks that don’t take hours at a stretch to complete. Afternoons are
too warm to sit outside and think anyway, so I move inside to work on
coding projects then. That gives me a schedule: I am willing to have
more interactions (and interruptions) in the morning than the afternoon
and I want to “reclaim” weekends. The next step is to tell Mail the
news.

image1I use the same email client (OS X’s Mail.app) to access all
of my mail accounts. Even though I use IMAP, this lets me read and
search old messages when I don’t have access to the network. The
brute-force way would be to manually change the preference “Check for
new mail” to the appropriate schedule. I hate doing things like this
manually, though.

I’ve done some work with AppleScript and Mail in the past. This
week (I’m not sure why it took me so long to figure out this approach) I
realized I could use AppleScript to control how often Mail checks for
new messages, and which accounts are checked.

Now that I have a general schedule identified, I can configure some
events in iCal using AppleScripts to control Mail. I began by composing
a few AppleScripts in Script Editor.

To check mail frequently in the morning:

image2

To check mail infrequently, for the afternoons:

image3

To turn of automatic checking entirely in the evening:

image4

To disable my work account for the weekend:

image5

To enable my work account on Monday mornings:

image6

With the scripts in place, I configured events in iCal to run the
scripts to adjust my settings at appropriate times.

image7Every week day morning, I turn up the frequency to every 5
minutes. This ensures that by the time I am up and ready to look at
email, the mailbox is up to date.

image8Around lunch time, I turn the frequency back down to once
per hour. I find I don’t even notice the change, and when I come back
from lunch I am ready to settle in and concentrate. I don’t make it
through the afternoon without checking email, but stretching out the
time between checks does help.

image9And in the evenings, I turn email off entirely. Note that
this script only runs Monday-Thursday. On Friday, I leave Mail set to
check messages once per hour, since I do receive personal messages over
the weekend and I want to see those.

image10To avoid being sucked back into work, I disable that
account entirely. Of course, on Monday morning, I have a similar job
scheduled to run the MailCheckWorkEnable script to re-enable the account
for the week.

Disabling the account entirely seemed like a drastic step, but is very
effective. When Monday comes around, I am refreshed and ready to work
again. I do not miss any personal mail, and have not been tempted to
“just look at this one thing” from my work messages.

PyMOTW: os

The os module provides a wrapper for platform specific modules such as
posix, nt, and mac. The API for functions available on all
platform should be the same, so using the os module offers some
measure of portability. Not all functions are available on all
platforms, however. Many of the process management functions
described in this summary are not available for Windows.

Read more at pymotw.com: os

Things to Do

In no particular order:

  1. Cull my Google Reader subscriptions. 364 is too many.
  2. Finish reading Dreaming in Code.
  3. Add tagging support to codehosting.
  4. Verify all of the domains under my control with Google Web Master
    tools.
  5. Create a Trac plugin for code reviews based on the process we use
    at work.
  6. Change the monitor feeds on CastSampler.com so they do not include
    items without enclosures.
  7. Enhance BlogBackup to save enclosures and images linked from blog
    posts.
  8. Write a tool to convert an m3u file to an RSS/Atom feed for
    Patrick so he will set up a podcast of his demo recordings.
  9. Improve AppleScript support in Adium.
  10. Add support to Adium for notifications when a screen name appears in
    a chat message.

Archiving mail on IMAP server

Version 1.4 of MailArchiveByDate now supports archiving to IMAP
folders.

It works exactly as expected for me, but some users have reported
problems with the script not creating new mailboxes. If the mailbox
exists, the messages are moved. I have not been able to reproduce the
problem myself using dovecot on Fedora Core 4 or Scalix (which is
supposed to look like an Exchange server).

Hawk Wings and Mail Archiving

I’m a little behind on my reading, so I just noticed that Tim over at
Hawk Wings is linking to MailArchiveByDate. I guess that
explains the email I’ve received in the past few days asking for help.
:-)

Tim comments that my update notes for the 1.2 release don’t convey
much meaning. I looked, and sure enough, they aren’t end-user-ready
release notes. I’ll try again. The original version of the script asked
Mail for the current selection. That meant, if you triggered the action,
then changed the selection by clicking on the next message, the wrong
message might be archived. The new version uses the selection
information passed to the script as an argument, so it is more reliable.

That’s still not very interesting, unless you are writing your own
AppleScripts. Suffice to say, if you are planning to giving it a try,
download the most recent version posted.

Adium 1.0

My ChatMonitor script does not work with Adium 1.0. It looks like
the “chat” object no longer contains “content” objects. I haven’t yet
figured out how to get the content from a chat window. Oh, well. I guess
it’s time to order those OS X programming books!

Adium ChatMonitor

We recently set up our own Jabber server at work. For a short time we
had been using an IRC server, but decided for a variety of
non-technical reasons to switch to Jabber. The benefit is now I only
have to run one chat client (Adium). The downside, is I miss the
feature of Colloquy which had a special notification event for when
I was mentioned by name.

I searched for a while, but didn’t find any way to add such a
notification to Adium. I finally hacked something together using an
AppleScript triggered for every incoming message. I’m sure there must
be a better way to achieve the same results, but this
works. Eventually I should learn more about how to develop true OS X
apps using Objective C, and then I can create a real plugin to do the
same thing.