Wednesday, September 30, 2009

Cycling Mugs

I spent all morning on these:



The minimum pixel size option on Google image search is a tremendous boon to the casual zazzler.

Monday, September 28, 2009

Listen to the Rich Guy

Robert had shared this Joel Spolsky essay on "duct-tape programmers" the other day, and it was sort of bugging me. In searching for it again, I found that it's already received its share of criticism.

It did make me wonder what made Spolsky so willing to promote Jamie Zawinski's advice despite a lot of it appearing to me to be pretty badly misguided. And I guess it has to be because, whatever happened to Netscape later on, he got his zillions of dollars, making him a success by the only meaningful metric of capitalism. And therefore someone whose wisdom should be respected.

Friday, September 25, 2009

Finally!

Terrible piece in the Times about next Tuesday's runoff for comptroller. "Sparks finally fly," but not word one about whether any of the candidates' spark-emitting accusations might have some validity.

Sadly not unusual when it comes to political reporting, but weird in light of the fact that the same reporter profiled each of candidates John Liu and David Yassky in yesterday's edition. Those are good articles! Very informative! And yet from today's piece you'd think the Times couldn't care less about informing voters, four days before they go to the polls.

Tuesday, September 22, 2009

And in More Mixtape Non-News

I never listened to Lil' Wayne's "rock" stuff when it was coming out. How did nobody tell me about his take on an "End of the World as We Know It"/"We Didn't Start the Fire"–style laundry list of modern evils, "Politics"?


Love, hate, size, race, grime, grace, lip injection, lethal injection, lip injection
Lethal injection, your assumption, economical destruction, breast reduction, police corruption
Love, hate, size, race, grime, grace, crime rate, lip injection, lethal injection
Lip injection, lethal injection, breast reduction, police corruption, your assumption
Economical destruction, breast reduction, police corruption, your assumption, economical destruction
Amazing.

Friday, September 18, 2009

Dips of the Woooooorld

The French Dip, which is actually a roast beef sandwich that you DIP.

Invented by France, hence the name.

Dips of the World!

Artichoke Dip, coming to us from Middle America.

Thursday, September 17, 2009

Dips of the World

Dips of the World.

Guacamole of Mexico.

Google Fails Me!

My street cleaning program appears to be working just fine, except for the fact that for some reason Google never sent me either of my SMS reminders this morning. I looked at the calendar and they're set up and everything; I just never got the message. And it worked perfectly on Monday!

It cost me a $115 ticket for double parking, though thankfully my neighbor alerted me before they could tow my car.

Clearly, I'll need a text-messaging system more robust than reminders in Google calendars. AppEngine will let me set up cron jobs (nullus), which I should then be able to use to trigger a message via Zeep.

Tuesday, September 8, 2009

Another SMS Thing

I keep track of my checking account balance and track all my bills in a sparsely populated spreadsheet, with a row for each calendar day and a column for each category of credit or debit (paycheck, transfer to savings, gas bill, ATM, &c.). The resulting balance is tracked in the rightmost column.

My practice has been, when withdrawing money from an ATM machine, to pocket a paper receipt until such time as I find myself before my computer and may record the transaction.

So I made another App Engine service that lets me enter ATM withdrawals into a database,

Withdrawals web app showing a $101.75 withdrawal on 2009-09-03,

and publishes monthly accounts as XML,

XML listing same withdrawal,

which are then consumed by Google spreadsheets' ImportXML() function:

Withdrawal shown in spreadsheet.

I called it "A.T.Them," and once I had it working, I added an endpoint to handle Zeep operations. Now when I withdraw money, I can decline the offer of a paper receipt, saving some trees, and text "atthem 61.50" or whatever and it will pop up right in my spreadsheet.

I can't figure out how to post the source without Blogger mangling the HTML, but it's all trivial in any case. One note is that ImportXML() doesn't do any kind of authentication, so the monthly lists of all my ATM withdrawals are not protected by anything beyond my keeping the URI's at which they may be found a secret.

Likewise (and this vulnerability, such as it is, applies equally to any Zeep service), someone who knew the URI that handles Zeep requests and my Zeep subscriber ID could impersonate "atthem" messages and populate my spreadsheet with junk. It's easy enough to add some sort of challenge and response to Zeep operations, but I didn't bother.

Monday, September 7, 2009

Cyclones vs. Spinners

Sarah and I biked down to Coney Island yesterday for the Cyclones' final regular-season game. Playoff slots had already been determined, so there wasn't anything at stake, which might have been why both teams kept putting in new pitchers. It was weird.

Carlos Beltran was playing for Brooklyn on rehab. He had a solid hit for his second at-bat, and some good if unspectactular fielding, but I think he struck out swinging a couple times. Not really a big hitting day for anyone, actually, with a really strong wind coming in off the water.

Ultimately an exciting game, with Brooklyn attempting a rally in the ninth but unable to overcome the 4-2 deficit. We were glad we brought sweaters, and didn't stick around for the fireworks show scheduled for ninety minutes later.

The ride down was fun, it was our first time going down into Brooklyn from the new neighborhood, which really just means we take Bedford down and then go West on Caton Ave where before we would have been going East on it. I think next time I'm going to stay on Bedford and take Church instead; maybe it was just West Indian Day Parade traffic last night, but I think Caton is usually pretty crazy.

Friday, September 4, 2009

Okay, Now I'm Really Done

Finished porting my street cleaning reminder program to Python for AppEngine, where it seems to be working fine. One unfortunate thing about moving the original desktop C# 3.5 code to ASP.NET 2.0 was that I had to replace all my nice Linq queries with regular loops, so being able to make use of Python features like tuples and list comprehensions was enjoyable.

Here is the code, with my personal identification information removed, and which I hereby release into the public domain:


import cgi


from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app


import gdata.service
import gdata.calendar
import gdata.calendar.service
import atom.service
import gdata.alt.appengine


import string
import time


class Cleanendar(webapp.RequestHandler):
  
  def __init__(self):


    manager = CalendarManager()
    self.commands = { 'n': manager.north, 's': manager.south, '': manager.clear }




  def post(self):


    if self.request.get('event') == 'MO':
      if self.request.get('uid') == MY_ZEEP_UID:


        self.response.headers['Content-Type'] = 'text/plain'
        self.response.out.write(self.process_command(self.request.get('body').lower()))
        return


    self.response.clear()
    self.response.set_status(400)




  def process_command(self, body):


    if body in self.commands:
      self.commands[body]()
      return ''


    else:
      return 'Bad command [' + body + ']'






class CalendarManager:


  def __init__(self):


    self.service = self.get_service()
    (self.calendar, hcalendar) = self.get_calendars(self.service)
    self.id = self.calendar.id.text.split('/')[-1]
    self.holidays = self.get_events(hcalendar.id.text.split('/')[-1])


    self.prefix = 'Cleanendar: '
    self.begin = time.strptime(time.strftime('%Y%m%d', time.localtime()) + '1230', '%Y%m%d%H%M')
    self.end = time.strptime(time.strftime('%Y%m%d', time.localtime()) + '1400', '%Y%m%d%H%M')
    self.daynames = ['Mo','Tu','We','Th','Fr','Sa','Su']




  def get_service(self):


    service = gdata.calendar.service.CalendarService(MY_GOOGLE_ID, MY_GOOGLE_PW, MY_APP_ID)
    service.ProgrammaticLogin()
    return service



  def get_calendars(self, service):


    feed = service.GetAllCalendarsFeed()
    calendar = None
    holidays = None
    for i, c in enumerate(feed.entry):
      if c.title.text == "Cleanendar":
        calendar = c
      elif c.title.text == "2009 NYC Alternate Side Parking":
        holidays = c
    return (calendar, holidays)




  def north(self):
    self.set_days([0,3])




  def south(self):
    self.set_days([1,4])




  def clear(self):
    events = self.get_events(self.id)
    for i, e in enumerate(events.entry):
      if e.title.text.startswith(self.prefix):
        self.service.DeleteEvent(e.GetEditLink().href)




  def set_days(self, days):
    self.clear()
    self.add_event(self.make_event(self.begin, "Move car", days))
    self.add_event(self.make_event(self.end, "Move back", days))



  def get_events(self, id):
    return self.service.GetCalendarEventFeed('/calendar/feeds/' + id + '/private/full')




  def make_event(self, t, desc, days):
    event = gdata.calendar.CalendarEventEntry()
    event.title = atom.Title(text=self.prefix + 'Street cleaning')
    event.content = atom.Content(text=desc)
    event.recurrence = self.make_recurrence(t, days)
    event.reminder = self.make_reminder()
    return event




  def add_event(self, event):
    return self.service.InsertEvent(event, '/calendar/feeds/' + self.id + '/private/full')




  def make_recurrence(self, t, days):
    data = ('DTSTART;TZID=UTC;VALUE=DATE-TIME:' + time.strftime('%Y%m%dT%H%M%S', t) + '\r\n' +
      'DURATION:PT5M\r\n' +
      'RRULE:FREQ=WEEKLY;BYDAY=' + string.join([self.daynames[d] for d in days], ',') + ';' +
      'UNTIL=' + time.strftime('%Y', t) + '1231\r\n' +
      'EXDATE:' + string.join([time.strftime('%Y%m%d', d) + time.strftime('T%H%M%S', t) for d in self.make_exclusions(days)], ',') + '\r\n')
    return gdata.calendar.Recurrence(text=data)




  def make_exclusions(self, days):
    e = []
    for i, h in enumerate(self.holidays.entry):
      for t in [time.strptime(w.start_time, '%Y-%m-%d') for w in h.when]:
        if t.tm_wday in [0,3]:
          e.append(t)
    return e




  def make_reminder(self):
    return gdata.calendar.Reminder(minutes=10, method='sms')




application = webapp.WSGIApplication([('/', Cleanendar)],
                                     debug=True)


def main():
  run_wsgi_app(application)


if __name__ == "__main__":
  main()

Thursday, September 3, 2009

Victory!

Zeep did indeed do what I wanted, and I found a site that hosts ASP.NET for free for 90 days, so I can now set my street cleaning days from my phone!

So if I have it set for the South side of the street (reminding me to move the car on Tuesdays and Fridays), my calendar looks like this:

Week from a Google calendar showing reminders to move the car on Tuesday and Friday

And then I go somewhere and when I come back I park on the North side. So I text "cleander n" to 88147, and the reminders jump to Mondays and Thursday, (excluding any day in the green holiday calendar):

Same week with reminders to move the car on Thursday, with none on Monday because of Labor Day

Zeep recognizes my phone number and uses the "cleander" string to route everything following it to my ASP.NET page in an HTTP POST request.

When the message gets to my site, the "n" or "s" populate the calendar with reminders on the appropriate days, an empty text clears the calendar completely (useful for going on vacation), and anything else will result in an error response.

I've saved "cleander n" and "cleander s" as message templates on my phone, so now any time I park it's just a few quick clicks.

Of course it will all fall apart in 90 days when my AspSpider account is deleted, but in the meantime maybe I'll be able to find some other free hosting option: all I need is to be able to run some sort of code in response to a POST request; the code is in C# right now, but it's short, and Google has APIs for Python and PHP and Java and whatnot as well, so I should be able to find something that works.

Update: Okay, Google's AppEngine will totally do what I need. I'll try it out tonight. It's fun and amusing to cobble together a baroque solution like this all to accomplish something that would take no effort at all if stupid T-Mobile would let me run a program on my phone in the first place. but also sort of depressing that I have to.

Update Deuce: This makes me feel a little better...apparently I'd have to do something similar on the Apple phone, as they don't provide a calendar API. Android does, but it's undocumented, though I would assume they make syncing with Google Calendars pretty easy so maybe it's less work. Palm gets it right.

Wednesday, September 2, 2009

Nerdery

So after being stymied (is that anti-Semitic?) in my attempt to run an alternate-side parking reminder program on my phone, I finally decided to just write a desktop version:

Window for selecting side of street

It sets up recurring events for the beginning and end of street cleaning hours, with an SMS reminder 10 minutes before, and excluding any days listed on a user-specified "holidays" calendar.

So, almost fine. I'll still need to remember to run the program after I get home and park my car, whereas with the mobile application I could have done it right from the phone. But text reminders and not having to set alarms twice a day on street cleaning days gets me 80% of what I wanted.

Something I definitely want to look into is the possibility of using an SMS web gateway to control a similar program running on a web server. I don't know if there are any free web hosts that allow you to run .NET or Python programs...you'd think that kind of thing would be de riguer in this day'n'age, but you could fill a wikipedia with what I don't know about internets hostering.

Update: I forgot to specify that when I wrote "sets up recurring events" that I meant it adds them to a Google calendar. That's where I got the idea to exclude holidays, because there's already a public calendar I subscribe to with all the street cleaning holidays. I also neglected to include the name of the project, which is "Cleanendar" and makes perfect sense.