Hello Visitor! Login or Sign Up

The A.R. Development Blog

LinkedIn is a very useful tool....

Aug. 15, 2008, 10:15 a.m.

...but probably not in the way you're thinking. Networking, well maybe, though many people seem to think it's facebook for grown-ups and that it's a glorified popularity contest. Never mind the quality of the connections, just have a lot!

...No. More interesting and useful I think is the way people behave on LinkedIn. In the last few weeks I've received a number of Recommendation requests from former co-workers. I'm always happy to write people a (truthful) recommendation but am often surprised by how I'm asked and how things go after I have recommended them.

Firstly, I know LinkedIn makes it easy to blanket-request a whole bunch of people for recommendations but don't use this feature, really. You're approaching someone to say (hopefully) nice things about you, why would you send them the equivalent of a form letter? Are you crazy? This requires at least some personal touch. This recommendation is something you will want on your LinkedIn profile for life so why not take a few minutes to craft an individual request?

Secondly, if you're smart you want to use a recommendation to help you in some way. Random "Joe is a really great guy" recommendations are not going to help you unless what you're looking for is a boost to the ego (back to the Facebook thing again). Usually people want a recommendation because they are looking to change jobs or perhaps careers. If that's the case why not say as much in your request? "Hi Bob, XYZ Corp is going fine but I'm thinking of moving into project management, I'd really appreciate it if you could write a recommendation for me. - Joe."

Already I'm thinking that I should write about Joe's attention to detail and people skills. I'm not going to lie but I'm going to paint him in the best light I reasonably can.

Third, and most interesting of all is what happens AFTER you write people a recommendation. Will they write and thank you for it? Amazingly, unbelievably, inconceivably many people just don't. If I walked up to you in the street and said "Hi, I'd like you to know that I think you're a smart dresser and very good looking" you'd at least have the decency to say "Thank you" before you ran off at top speed.

How dumb are these people? One of the things you learn in life is that if you ask someone to help you and they deliver then they are actually more likely to help you again in the future. If Richard Branson is in a position to help you - ask him, you have nothing to lose and if he helps you he's likely to do it again. People like people that they can help, it makes them feel good.

But you can be pretty sure that if you ask me for help, I help you and you don't show some gratitude then I'm unlikely to be motivated to do so again. In LinkedIn, as in life, this is something that actually weakens your social network. I had the good will to write a recommendation, your lack of acknowledgment is destructive to that good will.

So just remember what your mother told you.

Say thank you.


Improved Tag Clouds

Aug. 15, 2008, 9:13 a.m.

I wrote before about creating tag clouds with Turbogears though the idea is the same in pretty much any language you use : Split the bucket of things that you have into smaller buckets, assign each bucket a size and then display them.

Really it's mostly about how you split the categories into the buckets, how many elements end up in each one.

Lets say you have a bunch of categories and each one has a number of elements within it. You want to display the category names with each name in a size relative to the number of elements in it.

My last attempt looked at the min and max number of categories and divided it into 5 buckets each identified by it's own CSS class. Here are those classes.

.smallestTag { font-size: xx-small;}
        .smallTag { font-size: small;}
        .mediumTag { font-size: medium;}
        .largeTag { font-size: large;}
        .largestTag { font-size: xx-large;}

so far so good. What gets put into what bucket was determined by whether you were a min value (smallestTag), max value (largestTag) or somewhere between the buckets (small, medium and Large Tags). Here's the code:

        allcats = GiftCategory.select()
                cats = [cat for cat in allcats]

                #find out what the max and min clicks are
                nums = [cat.clicks for cat in cats]
                maxn = max(nums)
                minn = min(nums)
                diff = (maxn - minn) / 3

                #Work out what category deserves which tag
                l = []
                for cat in cats:
                    if cat.clicks == minn:
                        klass = 'smallestTag'
                    elif cat.clicks == maxn:
                        klass = 'largestTag'
                    elif cat.clicks > (minn + (diff * 2)):
                        klass = 'largeTag'
                    elif cat.clicks > (minn + diff):
                        klass = 'mediumTag'
                        klass = 'smallTag'

The problem with it is that you can get a lot of bunching when your distribution is uneven. If your largest category has 100 items but the others are in the range 1-10 your tag cloud isn't going to have a lot of variation.

This happened to me a lot. So I figured better to split the distribution into an even number of buckets regardless of the distribution.

Enter Recipe 425397 Split a list into roughly equal-sized pieces on ASPN. The code is short if not exactly a simple read :

def split_seq(seq, size):
                newseq = []
                splitsize = 1.0/size*len(seq)
                for i in range(size):
                return newseq

So now we can split up our categories by the number of buckets we want easily and our old code is changed to (something like, not tested):

        tag_names = ['smallestTag','smallTag','mediumTag','largeTag','largestTag']

                cats = [cat for cat in GiftCategory.select()]

                #Get all the numbers in a big list
                nums = [cat.clicks for cat in cats]

                #make a unique list of the numbers so [1,1,1,2,2,3] = [1,2,3]
                n = {}
                for num in nums:
                    n[num] = 1
                nums = n.keys()

                #Get them ordered

                #Split them into our buckets (result is a list of lists [[1,2,],[3,4],...,[45,60],[100]]
                num_seq = split_seq(nums,length(tag_names))

                #Assign each bucket a tag name
                num_seq = zip(num_seq,tag_names)

                l = []
                #Look through each category
                for cat in cats:
                    #Check each sequence to see if the count is in that sequence-list
                    for seq,tagname in num_seq:
                        if cat.clicks in seq:

Overall the result should be better for uneven distributions and give a more pleasing effect.


Custom Rendering for Radio Buttons In Django NewForms

Aug. 15, 2008, 8:06 a.m.

Django is a great framework and I like the NewForms library a lot, especially the as_table() and as_ul() type form rendering shortcuts but sometimes you don't want the default rendering with labels and everything.

In particular, I was trying to get a questionnaire effect like the one below :

Radio Button Questionnaire

I'd really like to be able to do that with code like :

        {% for field in form %}
         <td>{{ field.label }}</td>
         {{ field }}
        {% if field.errors %}
         <td colspan="6">{{ field.errors }}</td>
        {% endif %}
        {% endfor %}

CSS removed for a bit more clarity.

What I really wanted was each radio button in the list of choices to go in its own table cell so that my {{field}} would wrap all it's own input boxes. I also wanted to set a css style to each of those input boxes.

Now I don't know if this is the best way but this is what I came up with. I'm writing this on a < 1.0 version of Django here so things can change.

from django.newforms.widgets import RadioFieldRenderer, RadioInput
        from django.utils.encoding import StrAndUnicode, force_unicode

        class MyRadioInput(RadioInput):
            An object used by RadioFieldRenderer that represents a single
            <input type='radio'>.

            def __unicode__(self):
                return mark_safe(u'%s' % (self.tag(),))

        class MyRadioRenderer(RadioFieldRenderer):
            def render(self):
                '''Outputs a <td> for this set of radio fields.'''
                return mark_safe(u'
'.join([u'<td class="qcol">%s</td>'
                        % force_unicode(w) for w in self]))

            def __iter__(self):
                for i, choice in enumerate(self.choices):
                    yield MyRadioInput(self.name, self.value, self.attrs.copy(), choice, i)

and then to use it (in my view) :

CHOICES = ((4,'Strongly Agree'),
                              (0,'Strongly Disagree'),)

        class LoveDjangoForm(forms.Form):
            a_question  = forms.ChoiceField(initial=2,label="Gotta love Django",choices=CHOICES,widget=forms.RadioSelect(renderer=MyRadioRenderer))

The key part is of course the widget=forms.RadioSelect(renderer=MyRadioRenderer)

You can see the result (and test your love style!) at this love style test page.

Not the most complex thing to puzzle out and it only took a few minutes. Overall, it's nice to have a framework that gives you useful shortcuts 90% of the time but doesn't handcuff you for the remaining 10%.


Elite Text Trading in Python

June 4, 2008, 6:13 a.m.

And now for something completely different...

This so emphatically isn't the place to put this but I'm in a spring-cleaning mood and I just have to get this off my computer and out of my mind. So I give you..

pytxtelite.py - A python port of Ian Bell's original C Text Based Elite Trading.


Memories. I still remember my BBC Model B computer with great fondness and a large part of that fondness was Elite. In it's day Elite was a revolution, a whole new genre of game. I never made it to Elite but I was at least Deadly (which frankly I thought was a lot cooler). I stumbled across Ian Bells pages one day when gripped by nostalgia and took a look at his text based elite trading program. I played with it a little doing some short trading runs. Then I took a look at the C code.

I hate C code.

I especially hate C code that is a thin port of Assembly Code. Not because I am some kind of hot-shot programmer who knows better but exactly because I am a programmer who knows his limitations. Those limitations include (but are not limited to) an inability to think in hexadecimal and to visualize the effect of bit-shifting and masking on numbers. Sure, I can work it out, but I can't THINK in it and that makes the process slow going.

So I ported it to Python (2.5, maybe earlier, your mileage will vary based on how much tinkering you want to do). I ported it to Python partly because it looked like an interesting challenge and partly because I thought it would be interesting to be able to do stuff to the Elite universe. Stuff that I would have loved to do when I was 14 like, hmm...

  • Write a program that would wander the elite universe trading and making me Rich! RICH!
  • Draw maps of the universe highlighting all the anarchic planets (you know, to plan my Elite "shoot a pirate" holidays)
  • List out all the goatsoup strings for all the planets (there's a small bug in my version of goatsoup by the way)
  • Mess with the economic model of Elite so that goods replenish more slowly or quickly at planets

What is it good for?

About as much as Ian Bells original text elite trading game. You can travel between the planets trading stuff and building your fortune but there are no graphics, missions, combat, mis-jumps or death through inept docking maneuver.

But you can also use it for all the things I mentioned before - documenting and tweaking the elite universe. It has a command line interface based on the Cmd class (thanks Doug for the excellent pyMOTW covering Cmd) but beneath that there is a TradingGame class that you should be able to drive programmatically for your own nefarious ends.

Bugs & Limitations

Ian Bells compiled version of txtelite.exe uses a random number generator that I don't have access to. When I compile his C code with LCC it uses a different random number generator. This affects only the fluctuations of commodity quantities and pricing. I was able to track down a generator for python that matches the LCC version but not for whatever his compiler uses. In Ian's latest txtelite version (1.3+) he provides an alternate platform independent random number generator which I include and activate as the default but some of the trading scripts provided on his site won't work with the commodity fluctuations that the new random number generator provides.

There is also a small bug in the goat-soup code for random names, due I think to the way C handles addressing items outside of arrays (e.g. array length is 10, you ask for the 13th item) but it's small and doesn't spoil the fun.

There may be other bugs floating in the code. Elite was built on ancient microcomputers like the 6502 in the BBC Micro. It depends on a lot of bit twiddling and the quirks of C types. Python doesn't have the same quirks so some subtle uses of over or underflow might have been missed.

What now?

There were a bunch of things I thought about doing to the game but decided against it. I wanted to build a model of the Elite universe in something more malleable (to me) than C, not create a new game from the trading engine of Elite.

If you do something interesting with it (including improving it) please post in the comments below.

Good luck Commander!


Another day of outage due to ThePlanet

June 4, 2008, 3:19 a.m.

Like the aftershocks of an earthquake ThePlanet had further problems today related to backup generators which caused AutomaticRomantic to be down for many hours. Reminders were delayed but everything appears to be back up now. Apologies to anyone inconvenienced.


The Planet's outage really hurts.

June 2, 2008, 4:32 p.m.

Over the weekend ThePlanet had a huge outage that has only just been restored. Apparently there was some kind of explosion in their Houston Texas DataCenter and they were forced to take down the entire datacenter.

What hurts is that the excellent webfaction hosts their servers at ThePlanet. Down went AutomaticRomantic along with thousands of other websites. It could have been worse, the servers could have been destroyed but as it was no servers were damaged and only ThePlanets infrastructure was fried.

I've never had a good experience with DataCenters. I've been with a company that moved from one datacenter because of a massive outage to another datacenter that suffered a similar 100% outage within 6 months of the move. Now ThePlanet. The whole 100% uptime thing is a myth. It doesn't exist in real life no matter how good the datacenters people, procedures or technology. Critical systems just fail.

So what is the answer? Maybe it's systems like Amazons EC2 and Google's AppEngine, highly distributed storage and (in the case of AppEngine) processing which allow you to build systems that can scale easily and withstand failures. Now Google or Amazon could have dataenters fail but they can afford the kind of geographical separation that would mitigate the problem. Writing apps for these kinds of systems is a different skill-set but I think there's safety in the cloud.


PyxBoard.com : Now you can subscribe to pyxBoard Changes

May 6, 2008, 7:42 a.m.

One thing that's bothered me about pyxBoard since setting it up is that people update pyxBoards and you have no way of knowing about it unless they send you an email.

So I just added the ability to subscribe to pyxboards to the app. This means you can now get an email notifying you whenever someone adds an image to the pyxboard or adds a comment. Should make sharing a pyxBoard a bit more fun.

Remember, one of the points of the pyxboard is that it's a shared space - anyone who knows the address of the pyxboard can send pictures to it. Group of people at a party or event with phone camera's? text/email the pics to the same pyxboard and have them all appear in that image. Then discuss in the comments.


PHP Rascals

May 4, 2008, 3:10 a.m.

Every now and again AutomaticRomantic emails me to say that it's got into trouble. This is great feature of Django actually, that it emails the admins whenever a server error occurs.

Inevitably though the problem is always the same, scallywags trying to inject PHP code into automaticromantic's views.

The method is simple. Take a url like /listtopideas which takes a query parameter "page" and instead of giving it an integer, give it a url that points to a PHP page with the following content :

<?php echo md5("just_a_test");?>

The intent is to get the server code to load an evaluate this url (i.e. run the code on that page). I understand that older versions of PHP had this kind of vulnerability. What is so scary about this kind of attack is that, were it to work, the code would be evaluated within the web-apps process. That is, it would have access to all database connections etc.

This example code doesn't do anything except output some weird characters to the screen - the effect of doing an MD5 hash on a test string but it would tell the would-be attacker all they need to know. The next url submitted would contain more malignant PHP code.

Django doesn't have this particular vulnerability so after a few attempts the rascals move on.

Sigh. I gotta tighten up the parameter checking in those views. I wish the Internet were a more friendly place.


Google loves my dead blog..Why?

May 1, 2008, 5:01 a.m.

One from the this-is-strange department.

In 2006 I started a blog on a very niche subject. The kind of niche that doesn't attract significant advertising and which is of very little interest to the average person. My last post to that blog was around the start of 2006 so really it's a dead blog. Nothing happening.

But I keep track of the stats for that blog and all my other web-projects at statcounter.com which frankly I like better than google analytics for the simple reason that it's fast and doesn't get too clever. GA is a great product I'm sure but I've always found it painfully slow.

Anyway, look at traffic pattern for my dead, very-niche blog:

dead blog growth

At first I thought the explanation would be that some high traffic site had posted a link to the blog but when I look at the logs it's all google. Maybe google changed their ranking method and I ended up a winner? Even so, why continual growth? I would have expected a sudden spike up to a greater level of traffic and then a kind of leveling off but I don't see that.

It's all very puzzling. What does it mean?


AutomaticRomantic : A Major Update.

March 19, 2008, 5:18 a.m.

It's been a couple of years and the web space has moved on, especially the python web-space. It was time to upgrade AutomaticRomantic. There were a few things I wanted to add and a few things that needed fixing. But I find that web-apps are kind of like a thread, you start pulling on it and soon everything you have unravels and you want to re-make it.

The template

I started out with a great template from Andreas Viklund but lately I have been playing with the YUI Grids and after struggling with the template for a while I decided to just rebuild it around the YUI grids. It was remarkably easy and allowed me to do a few things :

  • Reposition the side-information bar to the left with an ad-approved width
  • Easily switch between different main-section widths during testing.
  • Know that the reset CSS provided by Yahoo was going to reduce and browser compatibility problems.
  • Strip the CSS back to just what I needed.

To be fair, I could have done a lot of that with the existing template but ensuring that it worked on all the browsers I wanted to support would have been more pain with the original template. I left the attribution to Andreas in the footer of the site because the resulting website is still very much influenced by his design and he deserves the credit.

One of the things I like about the YUI grids is that Yahoo maintain them and even serve them for you. That's a risk, if Yahoo go down so does all my supporting CSS but it's a risk I'm willing to take. The idea that Yahoo will update the grids to support and work around issues in new browsers is wonderful. Outsourcing some of your web-design worries for $0. Thank you Yahoo!

Advertising / Monetization

The focus of AutomaticRomantic has always been providing romance-related reminders to the romance-impaired. I didn't want to change that, people are using it. But in the two years that I have been running the site the hosting bills have started to add up. That's fine, I can handle the $10-15 a month in hosting that it costs to run the site but over a lifetime it will add up and I'd like to make it self-sufficient in terms of hosting costs. I doubt it will ever pay back my time but I got an education from that so I don't begrudge it.

The site always had a number of potential revenue streams : AdSense ads, Affiliate sales links and an Amazon aStore. Part of the plan with AutomaticRomantic was to learn about these forms of eCommerce.

Well, after two years of running the results are in. Affiliate sales has made me exactly $5 and Amazon's aStore has earned me $0. AdSense is the only thing that has consistently made even a few cents a day.

So I've reworked the site and ejected all the Affiliate links and I must say, I feel cleaner for it. Maybe one day there will be a place for them again but right now, no thanks. Despite no results from the aStore I'm keeping it. I like the way it integrates and I want to see what else Amazon come up with in this space. Tracking the results takes no time at all (I just don't bother) unlike the affiliate sales which require login to a bunch of different affiliate networks to see how things are going.

AdSense revenues from this site aren't exactly exciting from a second-income perspective but given the level and type of traffic the site receives (turbogear-heads, djangonauts, pythonista's etc) it's not surprising that romance-keyword revenue's aren't popular. I'm keeping AdSense, it's effectiveness should float with the right kind of traffic.

Content is king

You knew that already right? The search engines work on content, the more and more unique the better. The search traffic that does reach the site shows me that people are looking for more than just romantic tools, they want to understand their relationships better. The programming side of AutomaticRomantic is fun but I also like to write, so I'm adding a blog and an articles section.


Let me say first, Turbogears is fantastic, but 0.8 which I have been using for some years now has it's issues, at least for me, and upgrading wasn't an easy option. I started a dalliance with Django for pyxboard and when I decided I wanted to add some new features to AutomaticRomantic, I just ended up going there because pyxboard didn't exercise much of Django and I wanted a larger project to work on.

So AutomaticRomantic got rebuilt in Django. About 90% of the code just transferred over. It was all python and since TG never invited the mixing of templates and code it was a smooth transition. I spent more time working on reworking the structure of the web templates and CSS than I did on porting the core TG app to Django. I really missed TG's "flash" but finding a replacement wasn't hard.

One of the nice things about Django is the feeds framework which made my plan to add RSS feeds for people's reminder lists really easy.

Overall, it was a fun couple of months of tinkering. There is still more to do of course but I want to concentrate for a while on traffic building and see if I can hone my skills there.


© 2006 - 2013 Automatic Romantic | Terms of Use | Privacy Policy | Developer Blog

Web Design Inspired by Andreas Viklund Some icons by Mark James