A Simple Plan

Posted in Software Development by Dan on February 17th, 2019

“You have customers, they pay you money for the product or service, and you get profits! It’s almost too simple to work.”


Build something of value and charge money for it. That’s the whole plan – minus some detail. No freemium loss leaders, no selling advertising space to the surveillance capitalism machine, just ask people for money and give them something worthwhile in return.

Since my previous post work and life have intervened to slow my progress, so there’s nothing to show yet as most of the code remains unwritten. The basic idea is to provide a SaaS product for managing sports tournaments – with a specific focus on social, youth and amateur tournaments. The kind of events that people give up their weekends to compete in. Events that the rest of the world takes little notice of but that are taken very seriously by those involved.

There are some products in this space currently but none of them are particularly compelling. Most tournament administrators seem to get by with a spreadsheet that they’ve cobbled together or inherited from some Excel wizard, handling registrations and entry fees is usually done manually, and often the only way of finding out what’s happening on game day is from the man on the crackly P.A. system in a tent on the other side of the field.

As somebody who has helped to run a couple of these tournaments and played or refereed in several others in different sports, I believe we can do better. Whether my idea of better resonates with the people who organise and compete in these competitions is the fundamental question to be answered.

Another question I’ve had to think about is who is it for? I’ve gone back and forth over whether to cast the net wide initially or stick to those sports I know well. After reading Seth Godin’s new book this week with its emphasis on addressing the smallest viable audience, I’ve opted for the latter, which means focusing on sports that have high participation in the UK to start with (most of these are also popular in other English-speaking countries outside of North America) – particularly football (soccer), rugby (in all its forms) and netball.

Over the two years or so that I’ve been ruminating on this idea, I researched over 100 available domain names. I ultimately decided that the word tournament should be part of the name and had a strong preference for a .com domain. Having registered my initial choice, and renewed it 12 months later, I eventually changed my mind and registered something else. And so the future of online tournament control is called Tournamenteer and will be found at tournamenteer.com.

Building a Product

Posted in Software Development by Dan on February 4th, 2019

If you have nothing worthwhile to say, then say nothing.

So it’s been a while. I wonder if anybody still has this blog in their RSS reader? Are RSS readers even still a thing?

For the last decade I’ve pretty much been my own boss. I make a modest living selling native app development services to companies who can afford to give their apps away for free in support of whatever their core business is. I get some choice in what I work on and I make sure not to work too hard so that I can make time for the non-work things I want to do. As a one-person lifestyle business, it’s fine. It doesn’t easily scale though. If I want to do something more substantial, I ought to be selling a product.

A lot of the non-work stuff I make time for in a typical week involves running around various fields in all weathers chasing various balls, raging against the dying of the light and trying to offset the negative effects of the software developer’s inevitably sedentary existence. And when that’s done I’ll often spend some time watching the professionals chase balls. In other words I like my sport. I’m not particularly good at it but as long as I can slowly improve it keeps me motivated to keep trying.

A mobile app developer who likes sports and wants to build a product? You can see where this is going. Except it’s not.

I gave up trying to make any money selling mobile apps years ago. Mobile users want free apps. They might grudgingly pay a one-off comically small sum as long as they get a lifetime of updates without further payment, but then you need huge and sustained volumes to make it work as a business. Even that’s not necessarily enough because the app stores take 30% of everything and retain veto power over everything you publish.

On the web you don’t need to ask for permission. Short of doing something properly illegal you can publish what you like. And if you can convince people to pay for it you get to keep almost all of the money.

It was at least a couple of years ago that I started seriously thinking about building a sports-related, web-based, Software-as-a-Service product (the domain name has expired twice since I first registered it). Initially I was unconvinced that I had a workable strategy. But as the idea has slowly evolved I’ve reached the point where I have a plan that I’m ready to commit to. In the post-Christmas lull of January I finally got around to making a serious start on building the product.

The specifics can wait for later articles. The purpose of this post is to commit to my plan of launching the product at some point in 2019. Hopefully I’ll have a first version with paying customers before the (northern) summer but it depends on my other workload. I intend to document my progress here, and probably also on IndieHackers. Follow me here, there or on Twitter if you’re interested in the journey.

Charity Football Competition – £1,200+ Cash Prizes – Help to Save a 16-year-old Girl’s Life

Posted in The Real World by Dan on August 9th, 2016

16-year-old Kelly Turner is unable to get the life-saving cancer surgery that she needs in the UK. Her only hope is to travel to the USA and pay to have the treatment done privately. That’s a hideously expensive option but it’s the only one she has. Kelly and her family are trying to raise £1 million to fund this last chance. Many generous people have already stepped up to donate and to raise funds but there is still a long way to go.

Every year Donations With A Difference runs a charity fantasy-football-type game to raise money for good causes. This year we have been able to cover the £1,200+ prize fund entirely through sponsorship which means that we can donate everything that we make to Kelly’s appeal.

So, if you’re in the UK or Ireland (unfortunately we can’t make it available in the US as we don’t want to go to prison there – but you can donate directly to Kelly here), please sign-up today. There are cash prizes every week, the chance to win additional money for a charity of your choice, mini-leagues to compete against your friends, and the warm glow of knowing you’ve taken Kelly one step closer to her goal.

No More Updates to ReportNG

Posted in Java by Dan on December 3rd, 2015

About 8 years ago I made the decision to use TestNG for unit testing a Java project at work because it offered a much neater approach to the problem than JUnit 3.x. The only problem was that the HTML reports it generated were ugly. So I quickly hacked together a plug-in using Apache Velocity that would present the information in a way that I preferred and called it ReportNG. There was nothing elegant about the solution and the reports were functional but not particularly attractive, but it scratched an itch. It seemed to be an itch that irritated a lot of other developers because it attracted probably more users than any other, better written code that I’ve open-sourced before or since. I have made no substantial changes to it in years but there are still people using it.

I don’t do nearly as much Java as I used to and where I do I am either not using ReportNG or it is sufficient as it is. For these reasons, and because it ought to be replaced by something better (which probably already exists somewhere – I haven’t looked), I wanted to make it explicit to people still relying on it that there will be no further updates to ReportNG. You have three options:

  1. Continue using version 1.1.4 and accept that it will never get any better.
  2. Fork it on GitHub and make whatever changes you like. The Apache licence is permissive enough to allow most things.
  3. Stop using ReportNG and find something better.

Looking to Connect with Mobile App Developers in Kent

Posted in Android, iOS by Dan on December 2nd, 2015

One of the problems with operating as a one-man software development company is that the pipeline of work can be quite erratic. When you’re responsible both for drumming up business and for delivering the work, it’s difficult to maintain a steady pace from month to month. Instead quiet months can be followed by periods when you have to turn work away as there are not enough hours in the day. I’m not yet at point where I feel able to commit to taking on permanent staff but I’d like to be able to scale up capacity from time to time. To that end I’m looking to make connections with local app developers (native iOS and Android) who are looking for freelance work now and in the future.

There’s no promise of immediate work (although it is a possibility), I’m more interested in long-term connections that could be of mutual benefit. In particular, I’m looking for introductions to app developers in and around Kent, with whom I can meet face-to-face should the need arise. East Kent (Canterbury area) would be perfect but anywhere this side of London is fine. So if you’re a local app developer, or know somebody who is, please get in touch (Twitter, LinkedIn, or send a message here) whether you’re looking for work right now or not. And if you do want some work, please include some examples of apps you have developed and how much you charge for your services.

Likewise, if you’re a graphic designer doing/interested in doing app work, I’d be interested in connecting with you too.

If you’re a software development outsourcing company, this invitation is not for you. I already reluctantly speak to dozens of such companies on the phone every month.

Stopping Being Part of the Problem

Posted in The Internet by Dan on July 27th, 2015

No trackers on this site.Some time ago I wrote about my issues with Google and its anti-privacy agenda. It was my intention to avoid using Google services where possible because I don’t like Larry Page’s vision and Google’s (and Facebook’s and others’) tracking of individuals’ activity across the web. I have very little to hide but that’s not the point.

Two of the biggest enablers of Google’s surveillance machinery are Adsense and Google Analytics. These services give Mountain View a foothold on millions of properties across the web, from which they can observe users activities and track them from site to site. Today I finally got around to removing these from my own sites. I never paid much attention to the analytics anyway and there are more privacy-friendly alternatives if required. Adsense never generated enough income to compensate for the cheapening of the message.

Other unrelated changes I’ve made to this blog include changing the canonical domain to blog.dandyer.co.uk instead of blog.uncommons.org (the old URLs will redirect). In addition, some time ago I disabled comments. There were occasionally a few worthwhile responses but over 99.6% of all comments submitted were spam. The bottom half of the web is generally a cesspit. If you feel the need to respond to something I’ve written, you can contact me on Twitter or e-mail dan at this domain.

Play Our Online Football Game and Help Raise Money for Charities

Posted in Python, The Real World by Dan on August 8th, 2014

If you’re not in the UK or you’re not interested in the Beautiful Game and helping charities then this post isn’t for you. I’ve generally tried to keep the articles on this site at least tangentially related to software development but on this occasion the link is a bit more tenuous than usual.

Super10 promo image

I’d like to ask you to consider playing our online charity football game, Super10. You can win a share of several hundred pounds in cash prizes and, more importantly, you can help to raise money for charities. The charity aspect is two-pronged. Firstly you can win money for one of the four organisations that the game is supporting this season (Demelza Hospice Care for Children, Kent, Surrey and Sussex Air Ambulance, Pilgrims Hospices, and homelessness charity Porchlight) and secondly all additional proceeds go to Donations With A Difference (DWAD), the newly-registered charity, of which I am now a trustee, that is running the competition. DWAD will use this money to make grants to individuals and organisations to support the improvement of physical health, mental health and education in the UK.

Super10 is a little bit like fantasy football except that instead of picking players, you pick ten clubs – seven from the English game and three from the top European leagues. These teams score points for you throughout the season. Compared to traditional fantasy football it’s a low-maintenance game as there are no substitutions to deal with each week and transfers only occur during the January transfer window. You can make your selections and mostly forget about them. That said, it does have a way of making you care deeply about the results of teams you previously had no attachment to. Never been to Peterborough? Doesn’t matter, you’ll still feel the despair as they concede a late equaliser to Port Vale.

So what’s the software angle? Well Super10 has been around for a few seasons, certainly much longer than I’ve been involved, but this year we’re trying to take it beyond its previously limited scope. That has meant getting it fully online including being able to take payments online so that we can extend the game to a much wider audience and therefore raise more money. To enable this I’ve built the responsive Super10 website using Python, Django and Bootstrap. Much of the website was in use for most of last season but now we’ve extended it to open up Super10 to the whole country for the first time.

We’re restricting Super10 to the UK only for now because we are not familiar enough with the laws governing this kind of competition in other countries. The deadline for entries is noon (BST) on Saturday 16th August. Give it a go and get your friends to play too.



A Sceptic’s View of Google Glass

Posted in Android, Hardware by Dan on July 18th, 2014

Back in 1999, I was one of a group of Computer Science students invited to visit the research labs of a large consumer electronics company. I don’t remember a great deal about the prototype products we were shown but I do clearly remember being told that wearable computing was the next big thing. 15 years later wearable computers are still the next big thing.

This time around the devices have made it beyond the lab with a slew of underwhelming “smart” watches already on sale. In addition, Apple has been rumoured to be preparing its own for years now. Google on the other hand has taken a different approach by creating a new category of product with Google Glass.

It’s been over two years since Glass sky-dived into public view but it remains subject to a pretty exclusive public beta that you have to pay a hefty premium to join ($1500/£1000). Until a few weeks ago Glass headsets could only be purchased in the US. They are now also available in the UK. On Wednesday evening Google held its first European Glass developer meet-up at Skills Matter in London.

Since first hearing about Glass I’ve been deeply sceptical about it. It’s clearly capable of doing a few neat things that could be useful in a few niche areas but it just seems so inessential, it looks thoroughly ridiculous, and the current price tag is not destined to appeal to sensible individuals. However, I’ve often been too dismissive of new technologies in the past, so I was prepared to at least give it a go.

There was a full house at the Skills Matter Exchange including several Glass-adorned Googlers and a surprising number of other people who had presumably been parted from a grand of their own money. Interestingly, almost everybody had opted for the version with the plastic lenses. When the alternative is the bizarre lens-less titanium forehead band with nose perch it is entirely understandable, whether you need vision correction or not. The spectacle facade makes Google Glass look a lot less conspicuously weird. It’s still not a good look though, even without the beyond-parody third-party add-ons.

Google developer advocates Hoi Lam and Timothy Jordan delivered a couple of presentations suggesting how you should approach building apps for Glass (or Glassware as Google likes to call them). One major drawback for those who might be interested in building these apps is that at present there is no direct way for developers to make money from developing Glassware. Presumably that has to change at some point but for now apps can only be distributed free-of-charge (subject to Google’s approval), and in-app advertising is, mercifully, banned.

Following the presentations, those of us who hadn’t experienced Glass firsthand were given the opportunity to try out the headsets. Due to time constraints and the number of people who wanted to have a go, we didn’t get long enough to be able to get a feel for what it would be like to have this thing on your face all day but here are a few things I noted that might be of interest to those who haven’t tried one of the devices yet.

  1. The early promo shots of Glass tended to avoid showing the battery pack that rests behind your right ear and those pictures that did show it made it look awkwardly bulky. In reality this battery is quite thin and the headset is not as heavy as it looks.
  2. As a POV camera, Glass works well. It’s very easy to take snapshots or video (although I didn’t get an opportunity to check the quality of the results on a bigger screen). Unfortunately, according to Scoble, the battery is only good for 45 minutes of video and it will cook your face in the process. If all you care about is POV photography there are probably much better/cheaper options available.
  3. In a room with a lot of background conversation, the voice recognition worked well. The microphone clearly does a good job of isolating the voice of the wearer.
  4. The “screen” was underwhelming, albeit in a poorly-lit environment. The resolution wasn’t great and the focus didn’t feel entirely comfortable. It may well have been possible to adjust the focus but I didn’t have the time to find out.
  5. The user interface doesn’t feel like it would scale well to having a large number of apps installed. At the moment there is a lot of swiping through cards in a linear fashion.
  6. Unsurprisingly, Glass appears to be tightly integrated with Google+.

I was never going to be a person who would consider paying £1000 for one of these devices but having tried it very briefly I’m now certain that I wouldn’t buy one at a lower price either, even if I ignored the way it looks. There is, as yet, no compelling use case for the average person. One of the main features is that you can get your e-mails, SMS messages and other Android notifications beamed directly on to your retina without having to remove your phone from your pocket. I really don’t have any need for that kind of urgency. I’d rather ignore interruptions until I choose to deal with them. That’s not to say that there aren’t people who could find a use for Glass given better battery life and a more attractive price tag, but for most of us it’s a clever solution in search of a problem – the hardware equivalent of Google Wave.


Deploying an APK to Multiple Devices/Emulators Simultaneously Using Ant

Posted in Android, Ant by Dan on January 4th, 2014

Following my detour to Djangoland, the last week or so I have been back in the world of mobile app development. One of the things I’ve been working on is updating an Android app that has two versions built from the same codebase. Part of this update has been to re-skin both versions of the app and to make sure that everything looks reasonable on various devices running versions of Android from 2.2 up to the latest 4.4. When I build a new APK I want to check it on each of the three devices connected to my machine. If I had more spare USB ports I would conceivably be testing with even more devices. In some scenarios I might also have one or more emulators running to test configurations not represented among my devices. Installing the new APK on each of these from the command line is cumbersome. The adb tool will only install on one device/emulator at a time and, if you have more than one connected, you have to specify a long unique device ID to tell it which one to use. You could write a bash script to retrieve the IDs of connected devices and emulators and to run the adb install command for each, but since I’m using Ant I have written a custom task to do the job. For bonus points I’ve made it issue the adb commands in parallel so that the total install time is determined by the slowest device and not the sum of all the devices.

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
 * Custom task that uses the ADB tool to install a specified APK on all
 * connected devices and emulators.
 * @author Daniel Dyer
public class InstallAPK extends Task
    private File apkFile;
    public void setAPK(File apkFile)
        this.apkFile = apkFile;
    public void execute() throws BuildException
        if (apkFile == null)
            throw new BuildException("APK file must be specified");
            List<String> devices = getDeviceIdentifiers();
            System.out.printf("Installing %s on %d device(s)...%n", apkFile, devices.size());
            ExecutorService executor = Executors.newFixedThreadPool(devices.size());
            List<Future<Void>> futures = new ArrayList<Future<Void>>(devices.size());
            for (final String device : devices)
                futures.add(executor.submit(new Callable<Void>()
                    public Void call() throws IOException, InterruptedException
                        return null;
            for (Future<Void> future : futures)
            executor.awaitTermination(60, TimeUnit.SECONDS);
        catch (Exception ex)
            throw new BuildException(ex);
    private void installOnDevice(String device) throws IOException, InterruptedException
        String[] command = new String[]{"adb", "-s", device, "install", "-r", apkFile.toString()}
        Process process = Runtime.getRuntime().exec(command);
        consumeStream(process.getInputStream(), System.out, device);
        if (process.waitFor() != 0)
            consumeStream(process.getErrorStream(), System.err, device);
            throw new BuildException(String.format("Installing APK on %s failed.", device));
    private void consumeStream(InputStream in, PrintStream out, String tag) throws IOException
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            for (String line = reader.readLine(); line != null; line = reader.readLine())
                out.println(tag != null ? String.format("[%s] %s", tag, line.trim()) : line);
    private List<String> getDeviceIdentifiers() throws IOException, InterruptedException
        Process process = Runtime.getRuntime().exec("adb devices");
        List devices = new ArrayList<String>(10);
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            for (String line = reader.readLine(); line != null; line = reader.readLine())
                if (line.endsWith("device"))
            if (process.waitFor() != 0)
                consumeStream(process.getErrorStream(), System.err, null);
                throw new BuildException("Failed getting list of connected devices/emulators.");
        return devices;

Once compiled and on your classpath, using it is simple:

<taskdef name="installapk" classname="yourpackage.InstallAPK" />
<installapk apk="path/to/yourapp.apk" />

Django ModelChoiceField and HTML <optgroup>

Posted in Python by Dan on December 21st, 2013

I’ve been dabbling with Django over the last couple of months. I’ve played around with it a couple of times previously but this time I’ve actually built something reasonably substantial, which has meant that I’ve had to delve a bit deeper. One of the minor problems I solved today was how to group items in the HTML <select> element generated by a form’s ModelChoiceField. HTML has the <optgroup> tag for this purpose.

It’s not immediately obvious how you can get ModelChoiceField to use optgroups without over-riding the render method and re-implementing the HTML generation yourself, or bypassing ModelChoiceField completely and building something based on the basic ChoiceField (which does support optgroups). The only potential solutions I found from searching took the former approach (here and here). The reason I’m writing this post is because I think I’ve found a better, more concise solution that might be of use to future searchers.

ChoiceField accepts a choices parameter to its constructor. In the simple case this is just a list of items (each item is value/label pair). However, it can also accept a list of groups where each group is a tuple consisting of the group label and a list of items. The problem is that ModelChoiceField is different in that it has a queryset parameter instead, so there is no way to pass in the group information.

However, a comment in the source code says that we can set a property called choices after constructing the ModelChoiceField instance and the queryset will be ignored. The HTML <select> will instead be populated from this data structure with <optgroup> elements as required.

Assuming we want to group items by a field on the model, we can build the list of tuples from the queryset by sub-classing ModelChoiceField and over-riding the constructor. In this example I’m assuming that the field is a list of countries grouped by continent, where the continent is just a text field on the country model.

from django.forms import ModelChoiceField
from itertools import groupby
from operator import attrgetter
class CountryChoiceField(ModelChoiceField):
    def __init__(self, *args, **kwargs):
        super(CountryChoiceField, self).__init__(*args, **kwargs)
        groups = groupby(kwargs['queryset'], attrgetter('continent'))
        self.choices = [(continent, [(c.id, self.label_from_instance(c)) for c in countries])
                        for continent, countries in groups]

In order for this to work, the queryset must be sorted by the field that it is to be grouped by so that items in the same group are adjacent. You can do this before you pass it to the constructor or you can change the code above to call order_by on the queryset.

« Older Posts