Blog

May 02, 2010 06:08 +0000  |  Family Stephanie TheChange.com 1

So the launch didn't happen as planned. Due to an absence of a few features, Annalea wanted to push back the date to May 15th and I had a hard time arguing with her on that one. The fact is that the site is very featureful and frankly, I just don't have enough time to invest in it to get it all the way to 100% done. As it is, we'll be launching on the 15th with a fraction of the features she wanted to have from the start.

One truth about web development though is that these things never go they way you plan them. Plan for a Facebook-like site and you'll never get there because the investment of time, energy and money is just so high that the site will never get off the ground without going live (at least partially) at first. So I'm not bothered by the fact that we've cut a number of features -- it's normal, and we'll launch on the 15th with something that's both useful and pretty.

This weekend, I'm up in Kelowna with The Fam. Bowing to pressure from the parents, I've brought Stephanie with me to finally meet them and it was a little rough on her but she's managed admirably. The full family showed up for dinner though, which was more of a surprise than she was probably prepared for, but she seems alright. Most of you reading this blog know that my parents are pretty cool people, but it's rough your first time out if you're in the Girlfriend role, and she seemed to do alright :-)

As for how we got here, yes, I drove. Some of you may find it hard to believe, but I am in fact a licensed driver, capable of traversing rain, snow, and hail-drenched highways -- a feat I was forced to perform yesterday, on a moonless night. The highways were really quite scary, but we did fine, so a big :-P to the doubters out there :-)

We've got one more half-day here tomorrow and then we're heading back home. I have to get some work done in that time, but tonight I think I'll just get some sleep because I'm really quite tired. Steph's hopped up on candy at the moment though so I may have to kick her out of the room. As a closing note, I'm sorry that I've not been posting much in here lately. I've just been too overrun with work to do on these three fronts. I'm not going to abandon this site, but it's likely that I won't be posting as often as I have in the past. I do have a private post up though for those who are interested :-)

March 16, 2010 05:40 +0000  |  Personal Life Self Development Self Reflection Stephanie TheChange.com Work [at] Play 3

I thought that I might take this opportunity to post a little catch-up. I've been neglecting this blog for a while now and since it serves a number of purposes, not the least of which is personal record given my shoddy memory I should at the very least keep it up to date with what's going on in my life.

I'm busy. Incredibly busy. Some time ago Shawna pointed me to an article about how busy people live a sort of half life. We never do anything completely, are never able to invest ourselves in something whole-heartedly because we're constantly juggling too much. This has always been a problem for me, but at least now I'm starting to recognise it. The next step of course is to do something about it... suggestions are welcome :-)

So, lets chronicle this latest incarnation of "busy" shall we?

Let's start with the awesome. There's a girl. I've mentioned her before, but now that we're officially together (Facebook says so!), I want to gush a little more again. All that stuff in the aforelinked post is totally true. My heart skips when she smiles and she gets excited about the coolest, nerdiest things. We have a lot in common and this has led to a number of fun conversations and potentially a new project or two. It's all that good stuff that a new relationship is supposed to be, with the promise of some staying-power to boot. I'm going to work really hard not to screw this one up :-)

There's also the paying job, a.k.a., my role as senior programmer at Work [at] Play. They've got me doing a lot of Drupal work lately (boo!), but in the wake of my trip up to DjangoSki, there's a chance that we'll be doing a big project in Django soon (yay!). This is rather exciting, since I'm probably the best versed Python/Django person in the office, so I'd be working in more of a mentorship role rather than just a grunt programmer.

Then there's my new company: theChange.com. Bigger, meaner than a job, starting a business is exhausting work. I suppose that it wouldn't be so rough if you could build the company 9-5, Monday-Friday, but when you have to eat, it's a little more taxing. We have 3hour meetings now, twice a week, after I've been working for a previous 8, and we've broken down the "stuff to do" list into 5 (well, 4 now) week increments in preparation for our Big Public Launch in April. Annalea is super-hardcore, and an amazing person to work with... I just don't know if I have the energy some days.

Lastly, my father is working on a project of his own that he hopes will help my parents retire. It's a complex machine that requires, at the heart of it, a Sheevaplug running software I've written to handle talking to a PLC and magnetic card reader. It's some fun and crazy code, and so far, it's mostly worked... mostly.

Outside of the above, much of my former priorities are fading away. My involvement with the Greens has tapered off considerably, though that's due in large part to the absence of an election (or sitting government) for some time. My work with the VPSN pretty much died over the summer with our last cataloguing of the CCTV cameras in the city. I don't really miss the VPSN stuff, but my work with the Greens was really rewarding.

I suppose that somewhere in there, I'm supposed to find some personal time, but if I ever do, it always ends up being an evening of me wrestling with the fact that I could(should?) be working on our site. Mentally, I'm rather worn out of late.

And that's it for me. I'll do what I can to come up with something super-exciting for my next post. ...or maybe it'll be a lame meme. I haven't done one of those for a long time. Suggestions?

January 03, 2010 12:07 +0000  |  Django Facebook Python Software TheChange.com Web Development 2

This is going to be a rather technical post, coupled with a smattering of rants about Facebook so those of you uninterested in such things might just wanna skip this one.

As part of my work on my new company, I'm building a syncroniser for status updates between Twitter, Facebook, and our site. Eventually, it'll probably include additional services like Flickr, but for now, I'm just focusing on these two external systems.

A Special Case

Reading this far, you might think that this isn't really all that difficult for either Twitter or Facebook. After all, both have rather well-documented and heavily used APIs for pushing and pulling data to and from a user's stream, so why bother writing about it? Well for those with my special requirements, I found that Facebook has constructed a tiny, private hell, one in which I was trapped for four days over the Christmas break. In an effort to save others from this pain, I'm posting my experiences here. If you have questions regarding this setup, or feel that I've missed something, feel free to comment here and I'll see what I can do for you.

So, lets start with my special requirements. The first stumbler was the fact that my project is using Python, something not officially supported by Facebook. Instead, they've left the job to the community which has produced two separate libraries with different interfaces and feature sets.

Second, I wasn't trying to syncronise the user streams. Instead, I needed push/pull rights for the stream on a Facebook Page, like those created for companies, politicians, famous people, or products. Facebook claims full support for this, but in reality it's quite obvious that these features have been crowbared into the overall design, leaving gaping holes in the integration path.

What Not to Do

  • Don't expect Facebook to do the right/smart thing. Everything in Facebookland can be done in one of 3 or 4 ways and none of them do exactly what you want. You must accept this.
  • Don't try to hack Facebook into submission. It doesn't work. Facebook isn't doing that thing that makes sense because they forgot or didn't care to do it in the first place. Accept it and deal. If you try to compose elaborate tricks to force Facebook's hand, you'll only burn 8 hours, forget to eat or sleep in the process and it still won't work.

What to Do

Step 1: Your basic Facebook App

If you don't know how to create and setup a basic canvas page in Django, this post is not for you. Go read up on that and come back when you're ready.

You need a simple app so for starters get yourself a standard "Hello World" canvas page that requires a login. You can probably do this in minifb, but PyFacebook makes this easy since it comes with handy Django method decorators:

# views.py
from django.http import HttpResponse, HttpResponseRedirect
import facebook

@facebook.djangofb.require_login()
def fbCanvas(request):
    return HttpResponse("Hello World")
Step 2: Ask the User to Grant Permissions

This will force the user to add your application before proceeding, which is all fine and good but that doesn't give you access to much of anything you want, so we'll change the view to use a template that asks the user to click on a link to continue:

# views.py
from django.shortcuts import render_to_response
from django.template import RequestContext
import facebook

@facebook.djangofb.require_login()
def fbCanvas(request):
    return render_to_response(
        "social/canvas.fbml",
        {},
        context_instance=RequestContext(request)
    )

Note what I mentioned above, that we're asking the user to click on a link rather than issuing a redirect. I fought with Facebook for a good few hours to get this to happen all without user-input and it worked... sometimes. My advice is to just go with the user-clickable link. That way seems fool-proof (so far).

Here's our template:

<!-- canvas.fbml -->
<fb:header>
    <p>To enable the syncronisation, you'll need to grant us permission to read/write to your Facebook stream.  To do that, just <a href="http://www.facebook.com/connect/prompt_permissions.php?api_key=de33669a10a4219daecf0436ce829a2e&v=1.0&next=http://apps.facebook.com/myappname/granted/%3fxxRESULTTOKENxx&display=popup&ext_perm=read_stream,publish_stream,offline_access&enable_profile_selector=1">click here</a>.
</fb:header>

See that big URL? It's option #5 (of 6) for granting extended permissions to a Facebook App for a user. It's the easiest to use and hasn't broken for me yet (Numbers 1, 2, 3 and 4 all regularly complained about silly things like not having the app instaled when this was not the case, but your milage may vary). Basically, the user will be directed to a page asking her to grant read_stream, publish_stream, and offline_access to your app on whichever pages or users she selects from the list of pages she administers. Details for modifying this URL can be found in the Facebook Developer Wiki.

Step 3: Understanding Facebook's Hackery

So you see how in the previous section, adding enable_profile_selector=1 to the URL will tell Facebook to ask the user to specify which pages to which she'd like to grant these shiny new permissions? Well that's nifty and all, but they don't tell you which pages the user selected.

When the permission questions are finished, Facebook does a POST to the URL specified in next=. The post will include a bunch of cool stuff, including the all important infinite session key and the user id doing all of this, but it doesn't tell you anything about the choices made. You don't even know what page ids were in the list, let alone which ones were selected to have what permissions. Nice job there Facebook.

Step 4: The Workaround

My workaround for this isn't pretty, and worse, depends on a reasonably intelligent end-user (not always a healthy assumption), but after four days cursing Facebook for their API crowbarring, I could come up with nothing better. Basically, when the user returns to us from the permissioning steps, we capture that infinite session id, do a lookup for a complete list of pages our user maintains and then bounce them out of Facebook back to our site to complete the process by asking them to tell us what they just told Facebook. I'll start with the page defined in next=:

# views.py
@facebook.djangofb.require_login()
def fbGranted(request):

    from cPickle import dumps as pickle
    from urllib  import quote as encode

    from myproject.myapp.models import FbGetPageLookup

    return render_to_response(
        "social/granted.fbml",
        {
            "redirect": "http://mysite.com/social/facebook/link/?session=%s&pages=%s" % (
                request.POST.get("fb_sig_session_key"),
                encode(pickle(FbGetPageLookup(request.facebook, request.POST["fb_sig_user"])))
            )
        },
        context_instance=RequestContext(request)
    )
# models.py
def FbGetPageLookup(fb, uid):
    return fb.fql.query("""
        SELECT
            page_id,
            name
        FROM
            page
        WHERE
            page_id IN (
                SELECT
                    page_id
                FROM
                    page_admin
                WHERE
                    uid = %s
            )
    """ % uid)

The above code will fetch a list of page ids from Facebok using FQL, and coupling it with the shiny new infinite session key, bounce the user out of Facebook and back to your site where you'll use that info to re-ask the user about which page(s) you want them to link to Facebook.

Step 5: Capture That page_id

How you capture and store the page id is up to you. For me, I had to create a list of organisations we're storing locally and let the user compare that list of organisations to the list of Facebook Pages and make the links appropriately. Your process will probably be different. Regardless of how you do it, just make sure that for every page you wish to syncronise with Facebook, you have a session_key and page_id.

Step 6: Push & Pull

Because connectivity with Facebook (and Twitter) is notonoriously flakey, I don't recommend doing your syncronisation in real-time unless your use-case demands it. Instead, run the code via cron, or better yet as a daemon operating on a queue depending on the amount of data you're playing with. However you do it, the calls are the same:

import facebook

# Setup your connection
fb = facebook.Facebook(settings.FACEBOOK_API_KEY, settings.FACEBOOK_SECRET_KEY)
infinitesessionkey = "your infinite session key from facebook"
pageid             = "the page id the user picked"

# To push to Facebook:
fb(
    method="stream_publish",
    args={
        "session_key": infinitesessionkey,
        "message":     message,
        "target_id":   "NULL",
        "uid":         pageid
    }
)

# To pull from Facebook:
fb(
    method="stream_get",
    args={
        "session_key": infinitesessionkey,
        "source_ids": pageid
    }
)["posts"]

Conclusion

And that's it. It looks pretty complicated, and... well it is. For the most part, Facebook's documentation is pretty thorough, it's just that certain features like this page_id thing appear to have fallen off their radar. I'm sure that they'll change it in a few months though, which will make my brain hurt again :-(

November 22, 2009 22:30 +0000  |  Capitalism Ethics TheChange.com 0

I've been thinking about my own views on capitalism lately. It makes sense, I suppose when you consider that I'm co-founding a for-profit company with an intent to build something so useful that my shares in it would be worth a great deal of money. Add to that the fact that I've been re-negotiating my employment contract with Work [at] Play so that I might better apply my time & energy to this new venture and how I view capitalism becomes pretty important.

Basically, I see my role in a capitalist system as one of maintaining fairness for those involved. In other words, I don't want to make millions of dollars (honestly, what would I do with it?) Rather, I just want to be sure that the profits generated from my work aren't being disproportionately distributed.

To me, any capitalist model should be founded on the understanding that all parties involved stand to profit from their work equally based on the risk endured and the effort applied. In an employer-employee situation, wages are more than the trading of time for money (though they're that too). Wages are also a statement that the employer acknowledges that they wouldn't have a company without the employee and that in recognition of this fact, a reasonable portion of the profits are allocated to them.

I think that a lot of people share my views on this, but don't realise it. They're bitter that person X is paid more or less than person Y but can't explain why. In a co-venture, a lack of understanding can kill the partnership, so knowing the motivation behind those involved is always a good idea. My partner and I are on the same page on this, and I honestly think that it relieves a lot of potential tension.

On a related note, I found this link today that I thought I'd share. It's loosely related, titled "How to Fix Capitalism".

November 04, 2009 07:02 +0000  |  Employment Scrubby TheChange.com Work [at] Play 3

I'm tagging this one as "Employment" for lack of a better word, but frankly, that's not really accurate. My work life appears to be rapidly branching away from the employer/employee relationship and into running the show myself. The question is becoming one of "how much time do I have?" rather than "with whom can I find work?"

That's right, I'm bringing back the old-school "don't start a sentence with a preposition thing. You're just going to have to deal ;-)

The details: three months ago I was just working at Work [at] Play as a senior software developer, and for all the griping I do about the neighbourhood and the office, it's really a pretty cool place to work. The truth of it though is that I felt like I was stagnating, not doing anything useful with my life, and what's worse, I was rotting like this in Vancouver. I was ready to get the hell out of here at the drop of a hat -- to go anywhere really, just as long as it was sufficiently urban, interesting and wasn't here.

That all changed when Melanie forwarded an interesting "job" posting my way. A young, local entrepreneur was looking for a technical co-founder for a new company wanting to encourage business to do the Right thing by making it profitable to do so. To use an idea from Paul Hawken, our company would help other companies grow like trees, with deep roots, rather than like grass with no sustainable future. The details are complicated, and still a little secret, so I can't share them here, but the point is that I've signed on to make this thing happen. It may implode, but I don't think it will, and in the mean time, I'll have the opportunity to Use My Powers For Good... and that's all I've ever really wanted anyway.

But now things are getting crazy. Less than a week since I've entered into this partnership, I've been contacted by two separate parties wanting me to serve in a senior technical capacity for their enterprises as well. All three ideas sound promising, two of them are Good companies, the third, while run by a good, honest, person I trust, is more about the money and less about Making the World Better. All three are offering very little if any money to start.

The truth is, I can't do all three and keep my job at Work [at] Play. I probably can't even do two, though it'd be nice since one of the other two can pay a little. As it is, I've talked to the brass at my current employer and asked them to figure out a way that I might be able to work 4days/week for them so I can devote two days each week to my new partnership, and while they're currently mulling it over, I'm reasonably confident that they'll find that it's good for everyone if we can make it work.

But for now? things are CRAZY. I honestly don't know what my situation will be in a few weeks. And strangely enough... I like it this way. Who knew?