Peter · of · the · Norse · Thinks · He’s · Smarter · than · You

He Might be Right

Recent Entries · Archive · Friends · Profile

* * *

The stuff that happened in the last post bothers me more than it should. The reason is poor timing. I figured all this out just before a brief bout of depression. (By brief, I mean less than a day.) For everyone who doesn't know (which is almost everyone), depression isn't "extra sad", it's "emotionless". (My own experiences with depression make Vulcans scary.) For about 5 hours, the only emotion I had was annoyance at Hangouts.

Over the course of those hours, the echo box of my otherwise empty emotional landscape intensified my feelings into blind rage. It's actually a bit scary. And this ball of hate is still with me. I've been working on it for months, but there's still a part of me that wants to work over the Hangouts team with a baseball bat. Yes, Google has screwed over it's customers, but that doesn't mean they deserve my hate.

* * *
* * *

I was forced to delete my Google+ account recently. The problem is it was interfering with work. My office has Gmail for Work because it’s cheaper and more reliable than Outlook. And it comes with several features too. We’re using Google Docs constantly, and alot of conversations in Google Talk. Or rather were. The problem is Hangouts. Everbody was upgraded to Hangouts, and it’s completly broken.

There are a shit ton of missing features: First, there are only two states: available and unavailable. XMPP has at least a half dozen: available, busy, away, idle, disconnected, mobile. And since Talk was built on XMPP, it meant you knew why someone wasn’t answering your IM. If the message was undeliverable, you were warned, but Hangouts claim that all messages will be delivered. This is a lie.

Second, it doesn’t use a “buddy” list. It will connect to anyone with a Google+ account. And the only identification is your name and photo.  So if there is someone who works at Google with the same name as you, you’re going to get a bunch of random IMs.

This hit the perfect storm at work. Since everyone has a personal account with the same name and a photo that matches, it’s impossible to tell if someone will get the message. I have never used Hangouts, but because I had logged into G+ on Chrome, and had not explictly logged out, I was showing up as “available”. I never saw any of these messages. I missed an important impromptu meeting because they contacted my private account.

So I tried to find out how to stop people from using my personal accout. I tried un-circling and blocking coworkers; that just made me “unavailable” as if I had logged out. The only way to remove myself of their list of contacts is to delete my account. And before you tell me that this is a lot of whinging for a free service, I’d like to remind you that my work account isn’t. In fact the problem only lies with the account that we are payng for.

This goes on the pile of things that make Google less useful to me. The new maps engine won’t let me save directions. Google docs no longer lets you save something as a template. Search no longer lets you know what’s an ad and what’s a search result until you mouse over. I’d rather not deal with them anymore, and have starting moving what I can away.

* * *
* * *

Yesterday was my birthday (I lied to Facebook), and that’s caused me to think about what the last year has entailed. I’d like you to humor me as I look back at what the past year of my life.

A typical day

Wake up, Breakfast, shower, etc.
Drive to work
More work
Drive home
Fix/eat dinner
Clean up the house
Struck with the sudden feeling that by throwing away and packing up my mother’s personal possessions, I’m intentionally forgetting her.
Eat junk food and watch television
Go to bed

I’m not sure what the cause is, but I’m suddenly nostalgic. Perhaps it’s a side effect of being in the mid-30s. I was seriously considering buying Now That’s What I Call Music of the 90s. If it weren't for Ace of Base being on it, I might have.

Hell, my company is moving to a new office a few blocks from my old high school, and when I passed it last weekend I wasn’t filled with rage at my time there. First time in 20 years.

* * *
* * *
I just heard one of my favorite bands in a commercial. Should I be happy that they’re getting money, or annoyed that the song was so butchered? It could be worse. Fans of Phillip Phillips must be pulling their hair out.
* * *
* * *

I was really looking forward to Tour de Fat this year. I even bought a glittering green cape for it. Ran into Aaron during the parade.

When I came back in the afternoon, I saw Aaron, but avoided him. I knew he was going to ask what happened to the cape. Why would you buy a glittering green cape and not wear it? I also knew the answer was “Things have changed since this morning”.

This is a problem. When things get tough like this, I retreat. I know I’m going to retreat even further in the coming days. Don’t let me.

* * *
* * *
I was passed by someone on a fixie.
* * *
* * *

My major Django project involves mixing Django urls with static. That is, I want /blog/ and /djpro/ to be handled by the same Python dæmon, but / to be the static file /html/index.html.

Turns out that’s not something you can do out of the box. mod_wsgi splits the url when you use WSGIScriptAlias and Django treats it as if you did an include in your URL. You can merge them together in your

from django.core.wsgi import get_wsgi_application
_application = get_wsgi_application()

# I found a thing at
# that merges the PATH_INFO & SCRIPT_NAME.
def application(environ, start_response):
    environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO']
    environ['SCRIPT_NAME'] = '/'
    return _application(environ, start_response)

Then, all you need is to do is use multiple WSGIScriptAliases on the same WSGIDaemonProcess.

* * *
* * *

The last time I talked about tags in Django, there was only one game in town: Django-tagging. Now there is Django-taggit. It does (almost) everything right: A relation manager is used, which shows up in forms and the admin site. A slug is included. And it prefers commas to spaces as a delimiter. However, it wants to be user compatible with Django-tagging, so spaces can be used. Fortunately, that’s a simple problem to fix. I replaced the parse_tags and edit_string_for_tags functions, and it works great. Also, it’s case sensitive. I replaced the appropriate code so that it matches on the slug instead of the name. This has the added benefit of knowing that ‘hip hop’ and ‘hip-hop’ are the same thing.

You might not want to go that far, but if you’re using tags in Django, you should take a look at Django-taggit. It’s tagging done right.

* * *
* * *

Sometime something happens that we can’t see because the effected part is scrolled off screen. Most of the time it doesn’t happen because the button to trigger a DOM change is part of the DOM element. But sometimes something in another window or frame will change the main view. I first noticed the problem when I was testing adding a new line at the end of the playlist. The new div was added below the bottom of the window, so I couldn’t see it. The answer is to scroll the window to show the element. There are auto-scroll jQuery plugins out there, but they are designed to replace anchor links. I would make my own plugin, but my requirements are orthogonal to a typical use case.

The first step is to find the where the top and bottom of the line we want to see are.

jQuery.fn.showLine = function() {
    var line_top = this.offset().top;
    var line_bottom = line_top + this.outerHeight() + 18;

The 18px added to the bottom is for the height of the insert link. It overlaps the next line a little bit. I also add for the height of the postition: fixed header.

    var win_top = $(window).scrollTop() + 45;
    var win_bottom = win_top + $(window).height();

    if (line_top < win_top) {
        var new_top = line_top - 54;
    } else if (line_bottom > win_bottom) {
        var new_top = line_bottom - $(window).height() + 9;
    } else {

If any part of the line is scrolled off the top or bottom, then we find the nearest scroll amount that will show the whole thing, plus a little bit for padding. Finally, we animate the scroll.

    $('body, html').animate({scrollTop: new_top}, 400);

I use $(window) to find the value, because it always works. But it doesn’t animate. So I have to use both body and html because different browsers work differently. One will respond, and the other will simply ignore scrollTop.

Watch for my next post where I rave about django-taggit.

* * *
* * *

Most calendar programs today accept URLs to iCalendar files to keep up to date on something. That’s how I get Radio 1190’s concerts. There are two ways to build such a thing in Django. You can either use a template like normal, and just set the content type to text/calendar. Or you can use vObject. I’m going with the first, because I’m just publishing a list of concerts. If there was any kind of feedback or incoming data, I would need vObject. In either case, make the URL end in .ics.

There are two good validators. is standards compliant. If there is a single thing off, it will let you know and point to the part of the spec you’re violating. only cares about whether it’s understood. Since user agents don’t follow the the spec precisely, you need to find those problems too. Use both. While they both find major mistakes, there are errors that only show up in one or the other.

I’m going to go though the template and tech you what each line does and how it is understood.

{% autoescape off %} Because this isn’t HTML
PRODID:Peter of the Norse wrote this though Django
You need a PRODID. Don’t use someone else’s.
X-WR-CALNAME:Radio 1190 Concerts A suggested name for the new calendar.
Since I only use one time zone, this acts as a hint for some user agents to speed things up.
BEGIN:VTIMEZONE This is a copy/paste of the time zone information. While required, it’s not actually used.
TZID:America/Denver You need to use a recognized time zone name. Some user agents don’t parse time zones and will fail if they don’t recognize it. Others don’t parse time zones when they know the name. Others ignore all time zone information when they see X-WR-TIMEZONE. And a small number actually use the VTIMEZONE.
{% for event in list %}BEGIN:VEVENT
Now we get into some actual code.
UID:Radio1190-concert-{{}} Each event needs a unique id. Life’s too short to develop a UUID.
{% if event.time %}DTSTART;TZID=America/Denver:{{|date:"Ymd"}}T{{event.time|time:"Hi"}}00
Some events have times; others don’t.
DURATION:PT2H We only know the start time. Two hours shorter than most concerts, but if we did four or five, every concert would overlap. I might even change it to one hour.
{% else %}DTSTART;VALUE=DATE:{{|date:"Ymd"}}
{% endif %}DTSTAMP:{{|date:"Ymd"}}T000000Z
This should be the last modified date, but we don’t keep that kind of information.
LOCATION:{{event.venue|escape}} I wrote a filter to escape newlines, semicolons, and commas. They have special meaning to ics files.
SUMMARY:{{ event.performer_set.all|join:"—"|escape }}
DESCRIPTION:{% if event.we_present %}Radio 1190 presents!\n{% endif %}{% if event.minimum_age %}{{event.minimum_age|escape}}\n{% endif %}{{|escape}}
{% if %}URL;VALUE=URI:{{|escape}}
{% else %}{% if event.venue.homepage %}URL;VALUE=URI:{{event.venue.homepage|escape}}
{% endif %}{% endif %}END:VEVENT
{% endfor %}END:VCALENDAR{% endautoescape %}

All the rest is more of the same. Since blank lines break some user agents, there are a lot of run-on code lines. It makes the code a mess. Run your results against the validators again and again. You will make mistakes more than once, even when you’re working off of someone else’s example.

* * *
* * *