XMPP Jabber Photo Module

January 31, 2009 by Collin Anderson

I was looking around for a XMPP vCard Photo Module in May of 2007, and could not find one, so I wrote my own. It uses the xmpppy python module for communicating with the jabber server. Once the connection is made with the server, call register_handler(session). It will then download avatars of people on the roster list. To get the filename of a person’s avatar, call get_photo(photo_hash).

import base64
import os
import sha
import xmpp

PHOTO_DIR = "./photos/"

PHOTO_TYPES = {
    'image/png': '.png',
    'image/jpeg': '.jpg',
    'image/gif': '.gif',
    'image/bmp': '.bmp',
    }

def append_directory(filename):
    return os.path.join(PHOTO_DIR, filename)

def register_handler(session):
    session.RegisterHandler('presence', photo_update_handler)

def photo_update_handler(session, stanza):
    JID = stanza['from'].getStripped()
    vupdate = stanza.getTag('x', namespace='vcard-temp:x:update')
    if not vupdate:
        return
    photo = vupdate.getTag('photo')
    if not photo:
        return
    photo = photo.getData()
    if not photo:
        return
    #download the photo only if we don't have a photo with that hash
    if not get_photo(photo):
        request_vcard(session, JID)

def get_photo(photo_hash):
    for ext in PHOTO_TYPES.values():
        filepath = append_directory(photo_hash + ext)
        if os.path.exists(filepath):
            return filepath

def request_vcard(session, JID):
    n = xmpp.Node('vCard', attrs={'xmlns': xmpp.NS_VCARD})
    iq = xmpp.Protocol('iq', JID, 'get', payload=[n])
    return session.SendAndCallForResponse(iq, recieve_vcard)

def recieve_vcard(session, stanza):
    photo = stanza.getTag('vCard').getTag('PHOTO')
    if not photo:
        return
    photo_type = photo.getTag('TYPE').getData()
    photo_bin = photo.getTag('BINVAL').getData()
    photo_bin = base64.b64decode(photo_bin)
    ext = PHOTO_TYPES[photo_type]
    photo_hash = sha.new()
    photo_hash.update(photo_bin)
    photo_hash = photo_hash.hexdigest()
    filename = append_directory(photo_hash + ext)
    file(filename, 'wb').write(photo_bin)

Message Queue

December 11, 2008 by Collin Anderson

I wrote some code for a group project that I am kind of proud of. It’s not very clean code, but it accomplishes something cool. It’s a way for a website to sent messages to a browser in real time, without the browser needing to constantly be checking to see if the website has a message that is ready to be sent.

We did most of our project in php. Here is our php code:

<?php include_once("json.php"); function get_url($url) { $output = array(); exec("curl " . $url, &$output); return $output[0]; } function msgq_new() { $id = get_url("http://127.0.0.1:8888/new/"); return $id; } function send_message($data) { return get_url("http://127.0.0.1:8888/post/" . urlencode(array2json($data))); } if (isset($_GET['action']) && $_GET['action'] == "wait_for_message") { header("Content-type: text/plain"); echo get_url("http://127.0.0.1:8888/wait/" . $_GET['id']); }

Here is the Javascript part. We were using the YUI library, but you could easily do this without it.:

function wait_for_message() {
  var id = document.body.id;
  YAHOO.util.Connect.asyncRequest('GET', '/wait_for_message.php?id=' + id, {success: function(response) {
    wait_for_message();
    if (!response.responseText || response.responseText == "\n") return; // Server sent a nop
    var data = YAHOO.lang.JSON.parse(response.responseText);
    window[data.handler](data);
  }});
}
YAHOO.util.Event.addListener(window,'load', wait_for_message)

Here is the Python part:

from time import time
from random import uniform
from Queue import Queue,Empty
from socket import error
from threading import Thread
from urllib import unquote_plus
from wsgiref.simple_server import make_server

clients={} # A Queue and a http server thread for each client

def send_to_all(msg):
    print "sending %r to all" % msg
    for k, x in clients.items()[:]:
        if (time() - x.last_get > 300):
            # Client has not asked for any messages for 5 minutes
            # Delete them.
	    x.active = False
	    del clients[k]
	    continue
        print "sending to %s" % k
        x.q.put(msg)
    print "finished sending messages."

def wait_for_message(q):
    try:
        #Wait for a new message.
        return q.get(True, uniform(55, 59))
    except Empty:
        return "" # no message within a minute, send keep-alive

def handle(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    path = environ['PATH_INFO']

    if path.startswith("/wait/"):
	id = unquote_plus(path[len("/wait/"):])
	if id not in clients:
	    clients[id] = Server()
	clients[id].last_get = time()
        print "%s is waiting for a message..." % id
        return wait_for_message(clients[id].q)

    if path.startswith("/new"):
        from hashlib import md5
        id = md5(str(time())).hexdigest()
        clients[id] = Server()
        return id

    if path.startswith("/post/"):
	msg = unquote_plus(path[len("/post/"):])
        send_to_all(msg)

    return ""

class Server(Thread):
    "A Queue and a http server thread for each client"
    def __init__(self):
        Thread.__init__(self)
	self.q = Queue()
        self.setDaemon(1)
	self.active = True
	self.last_get = time()
        self.start()
    def run(self):
        while self.active:
            self.httpd.handle_request()

def start():
    httpd = make_server('0.0.0.0', 8888, handle)
    Server.httpd = httpd
    httpd.serve_forever()

start()

This one changes my status message on Google Talk

September 30, 2008 by Collin Anderson
import xmpp

def set_status(username, password, status):
    jabber = xmpp.Client('gmail.com')
    jabber.connect(server=('talk.google.com',5223))
    jabber.auth(username, password, 'python_status_message')
    jabber.send(xmpp.Iq(typ='set', queryNS='google:shared-status',
        payload=[xmpp.Node('status', payload=status)]))

Timesheet program I wrote for Chris this summer.

August 25, 2008 by Collin Anderson
from datetime import datetime
import os

def load_people():
    lines = [x.strip() for x in file('people.txt') if x.strip()]
    data = {}
    for line in lines:
        print line
        parts = line.split(' ')
        data[parts[0]] = ' '.join(parts[1:])
    return data

people = load_people()

cur_year = datetime.today().year

def get_name():
    while 1:
        try:
            num = raw_input("Type your number and press enter: ")
            return people[num]
        except KeyboardInterrupt:
            raise
        except:
            print "Invalid number"

def newday():
    return datetime.now().strftime("%m/%d %a")

def dateofline(line):
    return datetime(cur_year, int(line[0:2]), int(line[3:5])).date().day

def input_time():
    while 1:
        try:
            time = raw_input("Enter the time that you finished working (ie 4:32): ")
            hour, minute = (int(x) for x in x.split(':'))
            if hour < 7:
                hour += 12
            time = datetime.now()
            time.hour, time.minute, = hour, minute
            return time
        except:
            print "Invalid time."

def time():
    return datetime.now().strftime("%H:%M")

def punchin():
    print "Punched In"
    return 'tI ' + time()

def punchout():
    print "Punched Out"
    return 'tO ' + time()

def parsetime(string):
    return int(string[2:4]), int(string[5:7])

def record_time(name):
    filename = "%s.txt" % name
    if not os.path.exists(filename):
        f = file(filename, 'w')
        f.write('%sn' % name)
        f.write(newday())
        return f.write(punchin())
    f = file(filename, 'a')
    line = [x.strip() for x in file(filename) if x.strip()][-1]
    if datetime.today().day != dateofline(line):
        times = line.split('t')[1:]
        if len(times) % 2:
            print "You did not punch out last time."
            time = input_time()
            new_time = 'O ' + time.strftime("%H:%M")
            f.write('t' + new_time)
            times.append(new_time)
        times = [parsetime(x) for x in times]
        punched_in = -1
        total_hours = 0
        total_minutes = 0
        for x in times:
            total_hours += punched_in * x[0]
            total_minutes += punched_in * x[1]
            punched_in *= -1
        total_time = total_hours + total_minutes/60.
        f.write('tTotal: %0.2f' % total_time)
        f.write('n')
        f.write(newday())
        return f.write(punchin())
        raise NotImplemented
    times = line.split('t')[1:]
    if times[-1][0] == 'I':
        return f.write(punchout())
    if times[-1][0] == 'O':
        return f.write(punchin())
    raise Exception("Invalid data: %s" % times)    

while 1:
    name = get_name()
    print name
    record_time(name)

Counties in the Mississippi River Valley

July 29, 2008 by Collin Anderson

Last Friday I took the code from an earlier go at this and made it filter census data by county to only have data in the target area. You can also view my results of the program.

#!/usr/bin/env python
import sys
import urllib

def dms(d,m,s):
    return d+m/60.+s/3600.

def parsedms(f):
    """
    Imports a coordinates from tsv file and returns it as a list
    """
    a=[]
    for line in f:
        t=line.rsplit("t")
        lon=dms(int(t[0]),int(t[1]),int(t[2]))
        lat=dms(int(t[3]),int(t[4]),int(t[5]))
        a.append((-lon,lat))
    return a

print >> sys.stderr, "loading river data"
river_file = urllib.urlopen("http://collincode.files.wordpress.com/2008/02/mississippi-river.txt")
print >> sys.stderr, "parsing river data"
river = parsedms(river_file)

def distance(Lon1,Lat1,Lon2,Lat2):
    """
    Calculates the distance between two coordinates
    Based on calcDistance in http://nmviewogc.cr.usgs.gov/javascript/aimsMap.js
    """
    import math as Math
    Lon1 = Lon1 * Math.pi / 180;
    Lon2 = Lon2 * Math.pi / 180;
    Lat1 = Lat1 * Math.pi / 180;
    Lat2 = Lat2 * Math.pi / 180;
    LonDist = Lon1-Lon2;
    LatDist = Lat1-Lat2;
    A = Math.pow(Math.sin(LatDist / 2),2) + Math.cos(Lat1) * Math.cos(Lat2) * Math.pow(Math.sin(LonDist /2),2);
    C = 2 * Math.asin(Math.sqrt(A));
    D = (3963.1676 - 13 * Math.sin((Lat1 + Lat2) / 2)) * C;
    return D

def dist2river(lon,lat,river):
    'returns the distance and closest point on the river to the point'
    smallest = distance(river[0][0],river[0][1],lon,lat) #check against initial point
    for point in river:
        d = distance(point[0],point[1],lon,lat)
        if d <= smallest: #see if this point is closer
            thepoint=point
            smallest=d
    return smallest, thepoint

COLUMNS = (
    (1, 2, 'state', str),
    (8, 71, 'name', lambda x: str(x).strip()),
    (72, 80, 'pop', int),
    (81, 89, 'houses', long),
    (118, 129, 'land', float),
    (130, 141, 'water', float),
    (142, 151, 'lat', float),
    (152, 162, 'lon', float),
)

def parse_line(line):
    return dict([(name, klass(line[s-1:e])) for s, e, name, klass in COLUMNS])

def parse_data(county_file):
    for line in county_file:
        if line:
            yield parse_line(line)

print >> sys.stderr, "loading county data"
county_file = urllib.urlopen("http://www.census.gov/tiger/tms/gazetteer/county2k.txt")
print >> sys.stderr, "parsing county data"
data = []
for county in parse_data(county_file):
    if county['name'].endswith(' County') or  county['name'].endswith(' Parish'):
        county['name'] = county['name'][:-len(' County')]
        data.append(county)

print >> sys.stderr, "calculating distances to river"
distances = {}
for county in data:
    key = "%s, %s" % (county['name'], county['state'])
    key = key.lower()
    dist = dist2river(county['lon'], county['lat'], river)[0]
    if dist < 305:
        distances[key] = dist

print >> sys.stderr, "processing your data"
def runfile(i, o):
    for line in i:
        line = line.strip("n")
        columns = line.split('t')
        county = columns[0]
        key = county.strip().lower()
        if key in distances:
            print >> o, "%.3ft%s" % (distances[key], line)

import os
for x in os.listdir('.'):
    if x.startswith('b'):
        print "running %s" % x
        runfile(file(x), file('o' + x, 'w'))

This one tells me the mac address (bssid) of the access point I am connected to with wireless zero configuration using wmi.

June 8, 2008 by Collin Anderson
import win32com.client

def mhex(mac):
    return "".join('%02x' % x for x in mac)

wmi2 = win32com.client.GetObject("winmgmts:!root/wmi")
adapters = wmi2.execquery("select * from MSNdis_80211_BaseServiceSetIdentifier")
for ad in adapters:
    print ad.InstanceName
    print ad.Active and 'Active' or 'Inactive'
    print mhex(ad.Ndis80211MacAddress)

So apparently unicode(string, ‘cp1252′) is what I want.

May 30, 2008 by Collin Anderson

So apparently unicode(string, ‘cp1252′) is what I want.

A neat way to iterate over two items of the same length

May 27, 2008 by Collin Anderson

I assume this is memory effeicient.

for line1, line2 in zip(file('file1.txt'), file('file2.txt')):
    print line1, line2

Generators

May 20, 2008 by Collin Anderson
def regular():
    print "hello"

def generator():
    print "hello"
    yield "goodbye"
>>> regular()
hello
>>> generator()
<generator object at 0x010D0508>
>>> list(generator())
hello
['goodbye']

Found this here.

Gotcha in default arguments

May 15, 2008 by Collin Anderson
>>> def test(arg=[]):
...     arg.append('hi')
...     print arg
...
>>> test()
['hi']
>>> test()
['hi', 'hi']
>>> test()
['hi', 'hi', 'hi']

Found this one here.