Anyway, in no particular order, here's what I want to get done:
Customize Twitter Friends Feed: I need to customize my twitter friends feed so that it won't spam my feed reader with @replies I don't care about. I need to set my preferences on a per-twitterer basis. I really enjoy all @replies from some of my friends, but not all. Maybe I'll break down and find an app that already has the feature, but I prefer having just one place aggregate all the feed activity I'm interested in. [Edit: Biz is going to do this himself!]Make a Dictionary Popup: I loved the DQSD mwd popup definition result. But merriam-webster.com has a history of changing the layout of their page so that maintaining the mwd.xml search became onerous. It's time to replace that popup definition with one from aonaware. Here's the API call I'm most interested in: DefineInDict, with "gcide" for the dictId. [Edit: Completed!]- iTunes library rsync with OpenTape: As originally hinted.
- Download and install postgresql-8.3.5-2-windows.exe (You shouldn't need the whole install.)
- Add C:\Program Files\PostgreSQL\8.3\lib to your PATH for libpq.dll.
- Add C:\Program Files\PostgreSQL\8.3\bin to your PATH for ssleay32.dll.
- Hide System32's copy of LIBEAY32.dll (If you're on a DELL, and that one is far smaller and older than the others.)
- Download and install PyGreSQL-4.0.win32-py2.5.exe
Downloaded PyGreSQL first, before realizing it had dependencies to a postgresql install. Got some nebulous ImportErrors when I tried to "import pg".
from _pg import * ImportError: DLL load failed: The specified module could not be found.Great. What module? Searching the web, I discovered I needed to install postgresql. So I did, but I still got the error, even after updating the system's PATH variable. (Note, sys.path and os.environ("PATH") are two different things.) Eventually I discovered this great tip:
Try doing the failing import from the Python command line. (Start->Python 2.5->Python (command line)). Doing that will actually display a modal dialog that says exactly which dll could not be loaded. Yay!
Finally, I got the LIBEAY32.dll error mentioned above, the exact text of which follows:
The ordinal 2821 could not be located in the dynamic link library LIBEAY32.dlland once I hid the old LIBEAY32.dll, I got pg linking with the correct libraries, I was good to go. Hope this helps somebody.
I like this look, because it's uncluttered. There are only two noticeable links when you hover over stuff. The text in the middle is a link to the original entry, and the little icon to the right takes you to my account at that site.
However, I decided it would be handy to be able to have lifestream pages that show only entries from livejournal, my family blog, delicious, or plurk, or any of the others. I made it so. Yay! There they are!
But now the problem: From where should I put links to them? It's the same issue as with the lifestream legend. The lifestream legend is meant to be just a little table to help the reader know which services are being tracked. It's a handy thing, but it doesn't belong on the lifestream page. So I left it out.
But it seems like I should be able to put links to the filtered pages somewhere. Hopefully from the lifestream itself, because that's the handiest. But a new link from that page will hurt the current design. Here are some examples that would put the link just to the left of the little icon at the end:
I don't know what I should do. In the picture above there's a picture of a funnel, which apparently is geek code for "filter." Below that is an icon of a page with a green "go" arrow (from famfamfam), and below that is a down arrow unicode character. Below that is a nebulous, mysterious box. I'm not really pleased with any of these. I like the feature, but just don't know how to expose it.
Do you have any ideas of what would look good? Should I leave those links out of the lifestream and put them in from the legend? Maybe I should change the link of the icon at the end to be to the filtered page instead of my account page at the remote service?
[Edit] Currently, I changed the link of the icon at the end to point to the filtered page. I'll see how that works.
I think I can be thought of as a late early-adapter. I thought I had a lot of original ideas as I made my lifestream, but it turns out that more often than not, somebody else had already implemented one of the ideas. Happily, no one seems to have made all the same decisions as me, so my effort wasn't wasted. For me, my lifestream really is the best lifestream ever! Here's why:
[Edit] I changed the icon link. Now they point to a filtered lifestream page.
The Best of Both Worlds
Jeremy implements his as an aggregation of RSS and Atom feeds with no persistent storage of previous entries. So, as newer entries are made, the oldest entries are lost forever. His lifestream is always only the most recent few entries. Jeff, on the other hand, implements his with APIs, so he has access to the complete history of entries for any account. I maintain mine with feeds, but I imported my entire history from many accounts. My lifestream is huge, and spans years, even though I just started it a couple of months ago.
Also The Best of Both Worlds
Jeremy's lifestream is handy, because it never becomes unwieldy. It'll always be about the same size. Jeff Croft's and Emily Chang's persist every entry and thus continuously grow. They paginate their lifestream. You can view page 234 out of 399, for example.
I decided that 98% of the time, I'm only interested in something I wrote down in recent memory. Say, the last four weeks. So I made that the index page of my lifestream. Just the 28 most recent days of my online activity. It make for a nice, small page.
But the other 2% of the time, I'm searching for something older, or I'm feeling nostalgic. So I put my entire lifestream on one page, too. Sure, it's big, and I'll never browse it from a phone, but modern web browsers are perfectly capable of downloading it and rendering it, and will be able to do so for years to come. The entire history really has the same appeal to me as being able to search through a diary.
Even if I decide to paginate it eventually, it'll be easy, the backend will facilitate that.
The Details Matter
Since I provide my entire lifestream on one page, I also made sure to include the year for dates that precede this year. (Eg., October 5th, 2006. Note that that uses the intra-page anchor, another important detail.)
My lifestream has a discoverable RSS feed too.
But you know what? Nobody'd want a feed of a lifestream that constantly updates for individual entries. That's one thing that really bothers me about sweetcron feeds. They're just too noisy. Update, update, update!
So the RSS feed for my lifestream only provides weekly updates. That's what I'd really want from a lifestream feed. Just some sort of nice regular overview of all the activity over a certain period of time. And its permalinks are intra-page links into the huge complete history page.
Some of the accounts that I include in my lifestream don't support user activity feeds. For example, YouTube's feed for each user's Favorited videos doesn't have "date-favorited" information associated with it. Since I wrote my own lifestream engine, I was able to work around that problem. I doubt that most lifestream services like FriendFeed would go to the lengths I did in ensuring that I get exactly the information I want, regardless of whether or not the site's feed or API supports it.
It Helps Me Find Things
Searching for things half-remembered turns out to be pretty successful at the lifestream. I sometimes don't know if I posted a link to delicious, or if I plurked it.
It Encourages Me To Write Better
I always think twice before I write a clever title to a tweet, plurk, or blog entry. I realize now that I may well be searching for that entry in the lifestream later, and the lifestream may only have the title. (The lifestream also contains actual content from the entries, but the content isn't presented in the web pages. So maybe the content will be searchable too, eventually.)
Cleverness is out. Accessibility and searchability are in when you have a persistant searchable lifestream. Now, I strive for clarity in my titles.
I also stopped services that cross-post from one service to another. Having the lifestream made the idea of cross-posting even more redundant. If my livejournal friends don't want to see my tweets, I won't force them to with LoudTwitter.
Think there's privacy online? Here are a few of the public feeds that track me. Since they're there already, I'm going to aggregate them into one place. It'll be like a digital diary that writes itself.
Edit: Ha, ha. I forgot my livejournal.
It seems like a good jumping off point. I'll improve it with various advanced features I've already implemented elsewhere like:
- Conditional GETs.
- Authentication, (cURL example)
- Accessing all feeds with a simplified interface.
Conditional GETs. Mark already did it.Authentication. Mark already did it.Content Normalization. (That's what I meant.) Mark already did it.
There's nothing left for me to do, but the stuff I can't do. Make it pretty.
If you use Plurk, you know how fun the service is. It's one of the most addictive sites around. But I'm not here to convince you to try it. I'm here to help those who are already there.
Plurk utilizes a point system called Karma. It's a finely tuned system that appeals to those personalities that are competitive or like to collect. I love the reward system associated with karma. I despise the penalty system associated with karma. For example, if you're away from the service for a few days, you lose karma.
If you're already on Plurk, you need Karma Medic. Karma Medic's plurk user name is dblumebuddy. Karma Medic watches out for all her friends. If they're away from the service for too long, Karma Medic administers Karma in their absence.
Finally, you're allowed to leave Plurk and return to it without feeling like you've been unduly punished.
Instructions
1. Friend (not Follow) dblumebuddy.
2. There is no step 2.
Karma Medic will* accept the friend invitation, and will begin watching out for you in about two hours.
What Happens
Karma Medic is a cron daemon that watches out for her friends every hour. She notices the following sorts of things.
- If you been plurking, that's great.
- If you Karma isn't going too far into the negative, that's OK, too.
- If she's helped you recently, she'll move along.
But, if you haven't been plurking, and your karma's going dangerously low, she'll administer a little karma love in the form of a plurk response.
Also, she likes to sing.
Leaving Karma Medic
There are two ways to leave Karma Medic's services.
- Unfriend Karma Medic. Karma Medic will forget everything she ever knew about you within an hour.
- Stop plurking. If you leave Plurk by just walking away for a few weeks, Karma Medic notices and stops watching out for you.
The Details
Karma Medic is a completely transparent service. You can see everything she knows. She was written in Python and uses YAML and JSON.
She can only see public plurks.
I intend to release Karma Medic as open source under the MIT license at Google Code. I just need some free time. She uses the Python variant of the Unofficial Plurk API.
*She's only accepting up to
So what are you waiting for? Go friend her! Help her help you!
There were already a couple Python scripts out there that connect to Plurk, but nothing that constituted anything like an API like Ryan Lim's PHP version.
It's only a partial port, and it's not documented yet. But it's pretty easy to understand and it's usable. If you use it, please conform to Plurk's Terms and Conditions.
Here are some examples:
import plurkapi
p = plurkapi.PlurkAPI()
## View some plurks
for plurk in p.getPlurks(your_userid):
print plurk['content']
## Retrieving Karma
p.login(nickname, password)
print "%s has %1.2f karma." % (p.nickname, p.uidToUserinfo(p.uid)['karma'])
With Foxmarks, you can sync your bookmarks to their servers, or you can sync to your own webdav server. I prefer to host my own stuff, so I sync to my own webdav server. The bookmarks are stored in JavaScript's json file format.
But, that doesn't give me the option to browse to the server and easily see the other set of bookmarks. (If I sync'ed to Foxmarks' servers, then I could see them at my.foxmarks.com if I login with my other username.)
What's a Pythonista to do? Write a script that displays the bookmarks as URLs on a webpage! Sweet! Now I host my own repository for my bookmarks, and they're available online in case I need to see the other set's bookmarks. Here's the script:
#!/usr/bin/python
# foxmarks_reader.py by David Blume
import urllib
import simplejson # http://www.undefined.org/python/
import types
urls = ( ('Home Computer', r'http://user:pwd@url.com/fm/foxmarks.js
('Work Computer', r'http://user:pwd@url.com/fmw/foxmarks.j
)
def do_insert(folders, command):
if 'nid' in command:
args = command['args']
if args.has_key('ntype'):
if args['ntype'] == 'folder':
my_folder = []
folders[command['nid']] = my_folder
if command['nid'] != 'ROOT':
parent = folders[args['pnid']]
parent.append((args['name'], my_folder))
elif args['ntype'] == 'separator':
parent = folders[args['pnid']]
parent.append('---')
else: # 'bookmark'
parent = folders[args['pnid']]
parent.append('<a href="%s">%s</a>' % (args['url'], args['name']))
def print_folders(folders, indent):
for item in folders:
if type(item) == types.ListType or type(item) == types.TupleType:
print '%s%s' % (' '*(indent * 4), item[0].encode('iso-8859-1', 'replace'))
print_folders(item[1], indent + 1)
else:
print '%s%s' % (' '*(indent * 4), item.encode('iso-8859-1', 'replace'))
if __name__=='__main__':
print "Content-type: text/html; charset=ISO-8859-1\n\n"
print '<pre>'
for url in urls:
print '<h2>%s</h2>' % url[0]
sock = urllib.urlopen(url[1])
json = simplejson.load(sock)
sock.close()
folders = {}
for command in json[u'commands']:
do_insert(folders, command)
print_folders(folders['ROOT'], 0)
print '</pre>'
- Mood:accomplished
Completely different, but something that makes me just as happy: This video of voiceless talking hits me as hard as the first time I saw the multi-touch demo from the TED conference in Monterey.
Being able to help people with ALS in the near term rocks. The longterm potential for hardcore geeks is just icing. (Imagine how frustrating it'd be for teachers when their students are collaborating during tests without looking, moving or talking!)
- Mood:
happy
We've been displaying the Avid World Wide Weather Competition on our printer at work for some time. It was great fun in the beginning. The printer only displays the site with the best weather, and then the site with the worst weather, leaving out all the others.
But who wants to have to travel to the printer to know where they're glad they're not? Now, I bring the printer's weather to you, whereever you are! Introducing: The world's first printer weather feed.
Or, here's just a slightly better explanation of what I made.
from socket import *
import sys
printer = "10.1.1.2" # Change this to the IP of your printers
msg = "INSERT COIN" # Or, AYBABTU for longer displays
if __name__=='__main__':
if len(sys.argv) == 3:
printer = sys.argv[1]
msg = sys.argv[2]
s = socket(AF_INET, SOCK_STREAM)
send_message = \
'\x1b%%-12345X@PJL JOB\n@PJL RDYMSG DISPLAY="%s"\n@PJL EOJ\n\x1b%%-12345X' % (msg, )
s.connect((printer, 9100))
s.send(send_message)
s.close()
For what it's worth (in bandwidth credits), Chuck Shepard's News of the Weird successfully returns HTTP code 304, and my daemon honors it. So we've got bandwidth green credits to sell.
But yesterday, my daemon got its first URLError -2 (Name or service not known). Geek that I am, it gave me a little thrill to see how the exception was handled, and to add a little code that'll further isolate the exception. Whee! I program all day at the office, then go home and have fun programming a little more. I'm easy to please.
>>> def foo(a): a = 2
...
>>> x = 1
>>> foo(x)
>>> x
1
And:
>>> def bar(b): b.append(2)
...
>>> y = []
>>> bar(y)
>>> y
[2]
Alex Martelli dismisses newbie "by value or by reference" questions with the following:
The terminology problem may be due to the fact that, in python, the
value of a name is a reference to an object. So, you always pass the
value (no implicity copying), and that value is always a reference.
I find it simpler to explain as: the semantics of argument passing are
_exactly_ identical to that of assignment (binding) to a barename; you
can fruitfully see argument passing as local (bare) names of the called
function being assigned initial values by the caller (that's exactly
what happens, in practice). Now if you want to coin a name for that,
such as "by object reference", "by uncopied value", or whatever, be my
guest. Trying to reuse terminology that is more generally applied to
languages where "variables are boxes" to a language where "variables are
post-it tags" is, IMHO, more likely to confuse than to help.
I'm looking closely at LibraryThing (old, popular), Shelfari (newer, prettiest, not so fully featured), and Listal (newer, pretty, very general). It'd be fun to share and compare books. Do any of you use services like these?
def fprintf(file,fmt, *args):
file.write(fmt % args)
def printf(fmt, *args):
apply(fprintf, (sys.stdout,fmt)+args)
But no more.
Color my computer compromised. (Which is no big deal, since it has a sandbox on it anyway.) I'm now using Simon Brunning's excellent Python module winguiauto to take care of the old passphrase inconvenience.
Ah, never mind. Let me just publish one entry from the script's prefs file:
# yaml preferences for FeedMaker
# http://www.yaml.org/
# This file is read in and written by the FeedMaker python script
---
site:
- url: http://www.[namewithheldtoprotecttheguil tyyouknowwhoyouare].com
rss: [namewithheld].xml
etag: None
last_modified: Tue, 19 Sep 2006 06:45:28 GMT
md5_tag: <span class="titleHd">
md5: '\xa1KJ\r\xd6E\x03t\x03Fl>\xc7k\xcd\x08'The script takes that in, checks the specified tag's content, spits out an rss feed, and updates three fields in the preferences: etag, last_modified (for bandwidth-friendly conditional GETs), and md5 (fingerprint of specified tag's data). I've got two sites so far, but now I can at least have my machine easily monitor sites that don't publish their own feeds.
import operator
rows.sort(key=operator.itemgetter(4))
# or
rows.sort(lambda x, y : x[4] == y[4] and cmp(x[2],y[2]) or cmp(x[4], y[4]))
...not like I could just find the same info at the Python wiki or anything. :-P
