- Wednesday, 23 Sep 2015:
Digital in a Labour age
- Thursday, 18 Jul 2013:
Fun with Django storage backends
- Thursday, 18 Apr 2013:
Running statsd on Heroku
- Monday, 24 Dec 2012:
Accessibility Advent: normal accessibility advice still applies
- Friday, 21 Dec 2012:
Accessibility Advent: scrolling issues redux
- Thursday, 20 Dec 2012:
Accessibility Advent: only I get to put stuff in text inputs
- Wednesday, 19 Dec 2012:
Accessibility Advent: enhance long drop downs
- Tuesday, 18 Dec 2012:
Accessibility Advent: beware focus events, redux
- Monday, 17 Dec 2012:
Accessibility Advent: please avoid being clever, particularly with form elements
- Friday, 14 Dec 2012:
Accessibility Advent: show jump links on focus
- Published at
- Wednesday 23rd September, 2015
- Tagged as
- UK Politics
- Labour Party
- Digital Transformation
“If you are hopeful about exciting innovations that are more participative, a general election campaign is the last place you should go looking. They will happen in a less partisan race, with millions being spent not billions, and where the political situation is much more strange” — Tom Steinberg, interviewed in 2012
Since his victory, we’ve started to hear little nuggets from inside Jeremy Corbyn’s campaign to become leader of the Labour party. It was already clear to many the value of the strong and often grassroots social media power the campaign attracted; and the phone bank system written by a volunteer reminds me of aspects of Dashboard, the online system for campaign volunteers built by Obama for America in 2012.
With the new Labour front bench including a number of MPs who’ve been strong proponents of digital thinking for some time, and with experiments such as crowd sourcing PMQs, it’s a good time to think about how digital technology can benefit the Labour party.
However it’s more important to concentrate on what the Labour party and movement needs to be, and then think about ways technology can help. That’s what is driving the thinking behind pieces such as James Darling’s Open Labour and Peter Wells’ Growing an open Labour Party, but in an age of 140 characters and continuous partial attention it’s easy to bundle this up as “Labour in the digital age”, a convenient moniker that can lead the conversation adrift.
Digital technology, the things you can do with computers, phones, tablets (and watches, and Google Glass, and smoke alarms, and washing machines, and…) and the Internet, is revolutionary, increasingly pervasive, and often wildly misunderstood. But it is at heart only a tool; use it right and it can support what you want to do. Tech can be transformative in that it enables transformation; but it’s also possible to apply digital technology in ways that reinforce a status quo, that limit transformation.
Talking too much about digital tools can make us focus on the way we’ll achieve things that might not yet be fully thought through, and the outcomes may then not be what we actually want or need.
There’s also the risk that by concentrating on the digital, we fail to consider non-digital approaches. Technology, as Peter Wells points out, can be exclusive: not everyone is online. Not everyone wants to be online; and not everyone who is, wants to engage with politics online. That’s another reason to focus on the ends.
Also, when people talk about “tech” or “digital” they are often bundling together ideas that happen to be common in those spaces. For instance, “digital” can become a shorthand for agility or for disruption. I’ve also seen it be used as a shorthand for evidence-based policy. Not all those ideas are equal, or even the same type of idea. It’s also unlikely that there’ll be equal support for every concrete plan that stems from them or from political avenues that become feasible by digital technology (such as rapidly polling an electorate).
Bundling a range of ideas together under one label may be valuable when campaigning. It isn’t helpful when discussing how we want politics and government to work, too easily presenting a false view that you have to accept or reject the entire package. That’s often used deliberately as a rhetorical and political technique to avoid debate, but we want the debate and we’re better than that.
Some concrete ideas
Just because we should focus on ends not digital means doesn’t mean it isn’t worth having concrete proposals. Here are just a few that occur to me.
A pool of people willing to help
There are always jobs that need doing in any campaign or political organisation, and some of those for some time have required digital skills. The Labour party already provides training in some of these, but sometimes more specific talents are required, such as building a web app, or taking really good photographs of a candidate; this is by no means specific to people who can provide digital experience.
With some (fairly simple) digital tools allowing people willing to volunteer to update their profiles with availability, skills and talents, we wouldn’t be so reliant on little black books of campaign managers, candidates and other supporters. Some skill-sets don’t require people to be geographically close to the people they’re working with, so if done right and with enough volunteers this should enable any group to find someone to help them, on anything they need.
If there are tools, someone has to build them. Whether this is done by explicitly paying people or agencies, or as in-kind donations to the party, the people working on them should do so as part of a wider community of tool builders within the Labour movement, avoiding duplication, and providing more people who can work on any given tool.
I’ll go further and say that this should be an open source community. There will be some who believe this is unwise, providing succour to opposing parties, to which I have some counter-arguments:
- The main party in opposition to Labour isn’t remotely short of money. If they want a particular tool they’ll get it. The effort of volunteers, or paid staffers, will in any case often outweigh the cost of particular tools. Conversely, for local campaigns within our movement, removing the cost of tools they need may be the difference between existing or not.
- Reducing the cost of running political campaigns of any sort lowers the cost of politics. If we want the political system to work for everyone, we should want it to cost as little as possible.
- While some tools will be specific to the UK, not all will. If we want a Labour government for our country, we should want similar for others. Tools we build may be able to help, and tools that others build we will benefit from.
Some policies, widely accepted
As Peter Wells touches on in his article, we probably need some solid governance of things like privacy and data usage policies. People need to be able to trust what will happen to any data they give to any part of Labour, so they can make an informed decision on whether to give it or not.
We also need good standards for ensuring people aren’t overloaded by emails, as Giuseppe Sollazzo wrote in his summary of “digital in the campaigns” in August. In fact, this is just the bottom rung of how to think about communication within the movement. James Darling, commenting on the work needed to embed digital thinking, points out:
Labour movement’s digital revolution is only in its embryonic stage […] ‘Digital’ still means a website and a twitter account, those things understood by last generation’s most prestigious skill set; Public Relations
While broadcast communication is still going to be important, it absolutely cannot end there. That’s going to be a process of experimentation and discovery, but in the meantime I’d settle for getting regular emails when there isn’t a campaign on, and not getting four a day when there is.
Central support
If digital tools are being provided by the community, there may be a need for some centrally-supported hosting of those tools, particularly for smaller campaigns and organisations that just want something that works, and don’t need to dip into the pool of talent. (Particularly if we build the tools right.)
There’s also considerable value, in a social media world, to using the better-connected within the movement (some of which are at the centre, some not) to amplify the voices of those doing interesting work but who don’t have as wide an audience. This is of course how social media works in general, but I suspect there are specific things that can be done, such as actively seeking out interesting stories of issues, campaigns and successes to shout about and inspire people everywhere.
It should be possible, for instance, to provide tools, time and even funding to create videos and articles for online distribution, looking at people working to supplement underfunded council services, or to campaign on local environmental, fairness or access issues. Merely knowing that someone else has had success can encourage others to try the same elsewhere, and these local but incredibly valuable efforts aren’t going to make national newspapers or the evening news. Digital processes give us a huge opportunity to connect people with inspiration and support throughout the country.
The party already provides training and other services centrally; there’s nothing special about digital. (To provide maximum value, the talent pool of volunteers would need to run on a tool provided centrally, for instance.)
I’ve been into elections for far longer than I’ve been interested in the politics around them, if I’m being honest. Polling numbers, the Swingometer, even the timings and logistics around constituency counts can all become fascinating to someone who enjoys nerding out on numbers and data.
But a funny thing happened on the way to the all-night, emergency food-fuelled election watch (if becoming older and more interested in other people can be called “funny”), and over a period of some years I’ve found myself caring about political outcomes, not just for the results, but for the impact they can have on our country, our society and our future.
For a long while I felt that there wasn’t a party political position that matched my views (one of my blogs over the last couple of decades was subtitled “unfashionable politics”), but tides turn, just as I’ve come to realise that being part of a movement where you mostly feel comfortable is better than refusing to compromise, leaving you sitting on the outside, swearing in.
The last few years have been a fantastically interesting time in politics for someone with a digital technology background. US presidential elections since 2004 have made increasing use of technology, and by 2012 both Mitt Romney for President and Obama for America went heavy both on “back office” data analysis and more visible approaches on social media. UK parties haven’t been far behind, with Labour and the Liberal Democrats’ use of Nationbuilder, through Conservative use of highly detailed targeting on Facebook in the run-up to the 2015 General Election. Meanwhile, organisations like MySociety and Full Fact have been filling in some of the digital gaps in both accountability and civic services, while people like Democracy Club have been looking at the same around elections.
Now, it feels like there’s an opportunity for many more people to help out in UK Labour. Maybe that includes you. Maybe that includes me. And maybe I’ll see you in Brighton, to throw some ideas around. Just as long as we keep sight of the ends.
This was originally published on Medium.
- Published at
- Thursday 18th July, 2013
Django abstracts file storage using storage backends, from simple filesystem storage to things like S3. This can be used for processing file uploads, storing static assets, and more. This is just a brief look at some things you can do which are kind of fun.
Using Amazon S3
django-storages
is “a collection of custom storage backends”, including support for Amazon S3. You want to use the boto-based one, because it has lots of useful features. You can use it pretty quickly without customisation just by adding a few variables to your settings.py
; I tend to put AWS access keys in environment variables rather than have different settings.py
for different uses, because it plays better with Heroku.
AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']
AWS_QUERYSTRING_AUTH = False
AWS_HEADERS = {
'Cache-Control': 'max-age=86400',
}
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
# these next two aren't used, but staticfiles will complain without them
STATIC_URL = "https://%s.s3.amazonaws.com/" % os.environ['AWS_STORAGE_BUCKET_NAME']
STATIC_ROOT = ''
DEFAULT_FILE_STORAGE
is used when you want to store file-like things attached to your models, using field types like FileField
and ImageField
; STATICFILES_STORAGE
is where the static files pulled together from apps and your project by the collectstatic
command end up.
Okay, great. But say we want to do more?
Put static files in a slightly different place
If you subclass the S3BotoStorage
class, you can override some of its configuration. There are lots of these, but location
is an interesting one because it acts as a prefix for the keys stored in S3.
import storages.backends.s3boto
class PrefixedStorage(storages.backends.s3boto.S3BotoStorage):
def __init__(self, *args, **kwargs):
from django.conf import settings
kwargs['location'] = settings.ASSETS_PREFIX
return super(PrefixedStorage, self).__init__(*args, **kwargs)
So if we plonk a suitable bit of configuration into our settings.py
:
ASSETS_PREFIX = 'assets'
STATICFILES_STORAGE = 'prefixed_storage.PrefixedStorage'
then our assets will be separated from our uploaded media. (You could also put them in a different bucket, using the bucket
argument, for which you might also want to set access_key
and secret_key
differently to the default configuration we put in settings.py
earlier.)
Protect some file storage
Most of your media uploads – user avatars, for instance – you want to be public. But if you have some media that requires authentication before you can access it – say PDF resumes which are only accessible to members – then you don’t want S3BotoStorage
’s default S3 ACL of public-read
. Here we don’t have to subclass, because we can pass in an instance rather than refer to a class.
from django.db import models
import storages.backends.s3boto
protected_storage = storages.backends.s3boto.S3BotoStorage(
acl='private',
querystring_auth=True,
querystring_expire=600, # 10 minutes, try to ensure people won't/can't share
)
class Profile(models.Model):
resume = models.FileField(
null=True,
blank=True,
help_text='PDF resume accessible only to members',
storage=protected_storage,
)
There is no permanent publicly-accessible URL for the uploaded resumes, but it’s easy to write a view that will redirect to a temporary URL. Because we set up S3BotoStorage
to use query string-based authentication, when asked for the field’s URL it will contact S3 and ask for a temporary one to be created. The configuration above gives use 600 seconds, or 10 minutes, before that URL becomes invalid and can no longer be used.
from django.views.generic import DetailView
from django.http import HttpResponseForbidden, HttpResponseNotFound, HttpResponseRedirect
class ResumeView(DetailView):
model = Profile
def get(self, *args, **kwargs):
obj = super(ResumeView, self).get_object()
if not request.user.is_authenticated():
return HttpResponseForbidden()
if obj.resume is None:
return HttpResponseNotFound()
return HttpResponseRedirect(obj.resume.url)
Or you could just put it in a template, only for members:
{% if user.is_authenticated %}
<a href='{{ profile.resume.url }}'>Grab my resume</a>
{% endif %}
Making a staging version of your live database
This is something I needed to do recently for NSFWCORP: come up with an easy way of taking a live database dump and making a staging instance out of it. This is all run on Heroku, so moving the database dumps around is easy, and writing something to throw away all non-staff users, old conversation threads and so on is also simple. But I also needed to duplicate the media files from the live bucket to the staging bucket. My solution is as follows:
import os
import os.path
import shutil
import sys
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db.models import get_models, FileField
from storages.backends.s3boto import S3BotoStorage
class Command(BaseCommand):
output_transaction = True
def handle(self, *args, **options):
# we want a django-storages s3boto backend for live, using
# a dedicated read-only key pair
storage = S3BotoStorage(
bucket='nsfw-live',
access_key=settings.LIVE_READ_ONLY_ACCESS_KEY_ID,
secret_key=settings.LIVE_READ_ONLY_SECRET_KEY,
)
# now just go through all the models looking for stuff to do
for model in get_models():
fields = filter(lambda x: isinstance(x, FileField), model._meta.fields)
if len(fields) > 0:
sys.stdout.write(u"Copying media for %s..." % model._meta.object_name)
sys.stdout.flush()
for obj in model.objects.all():
for field in fields:
_if = None
_of = None
_file = getattr(obj, field.name)
if not _file.name:
continue
try:
_if = storage.open(_file.name, 'rb')
if not settings.AWS_AVAILABLE:
full_path = _file.path
directory = os.path.dirname(full_path)
if not os.path.exists(directory):
os.makedirs(directory)
if not os.path.exists(full_path):
with open(full_path, 'wb'):
pass
_of = _file.storage.open(_file.name, 'wb')
shutil.copyfileobj(_if, _of)
except Exception as e:
sys.stdout.write(u"\n failed %s(pk=%i).%s = %s: " % (
model._meta.object_name,
obj.pk,
field.name,
_file.name
))
sys.stdout.write(unicode(e))
finally:
if _if is not None:
_if.close()
if _of is not None:
_of.close()
sys.stdout.write("done.\n")
Note that there are three new settings.py
variables: LIVE_READ_ONLY_ACCESS_KEY_ID
and LIVE_READ_ONLY_SECRET_KEY
should be fairly obvious, and AWS_AVAILABLE
just tells me whether AWS support is configured in the environment, which I use to ensure the destination path and file exist in advance for local storage. I could avoid that by doing something like _file.save(_file.name, _of)
, although I’m not entirely sure that will preserve file paths and names. It’s cleaner though, and is probably a better solution.
Summing up
The Django storage API and pluggable backends gives a lot of flexibility in how you manage both static assets and file-like things. As well as django-storages
there are plenty of other options for when the built-in file system options aren’t suitable for you.
- Published at
- Thursday 18th April, 2013
statsd
is a “simple daemon for easy stats aggregation”: you send it stats whenever you can (such as when rendering a web page), and it aggregates them internally and passes them upstream to something that can store them and make them available for other clients for analysis, graphing and so on. Upstream stores from statsd
might include the Carbon storage engine from Graphite that you can run yourself somewhere, or a hosted service such as Librato. You can combine the two by using Hosted Graphite, which does exactly what it says on the tin.
Heroku is an infrastructure as a service company that provides an abstraction over servers, virtual machines and so forth geared to web deployment, as well as a toolchain for working with that.
It would be nice if we could use them together, and the good news is that we can. I wrote this because I couldn’t find anything online that spells out how to. The code and configuration is available on github.
How we’re going to do this
A simple deployment of statsd
is this: put one instance on each physical machine you have, and point them all at a storage system. (You can also chain instances together, and have instances send their data on to multiple receivers. Let’s just ignore all of that, because then you probably don’t want to host on Heroku, and if you do you can certainly figure out how this all applies to your setup.)
On Heroku, we don’t have physical machines; in fact there isn’t the concept of “machine” at all. Instead, Heroku has Dynos, which are described as “lightweight containers” for UNIX processes. From their documentation:
[A Dyno] can run any command available in its default environment combined with your app’s slug
(The slug is basically your codebase plus dependencies.)
When working with physical machines there’s a tendency to put a number of different types of process on each, to avoid having to buy and manage more of them. With virtualisation, and hosting systems such as Amazon EC2, this isn’t so important, and with Heroku their entire architecture is set up almost to mandate that you have different types of Dynos (called process types) for different jobs; almost always a web type that is basically your application server, probably a secondary worker type that handles any long-running operations asynchronously to web requests, and so on.
However this doesn’t mean we can’t run multiple UNIX processes within one Dyno. Providing each process type is still only doing one thing, it still fits the Heroku semantics. This means we can tuck a statsd
instance away in each Dyno, so it will aggregate information from the work being done there, with each statsd
sending its aggregated data upstream.
(Why not have a process type for statsd
and send all data to one or two Dynos before aggregating it upstream? Because statsd
works over UDP for various sound reasons, but Heroku doesn’t provide UDP routing for its Dynos. Even if it did, you wouldn’t want to do things that way because UDP between arbitrary Dynos running who knows where within Heroku’s virtualised infrastructure can fall foul of all sorts of intermediate network issues.)
A demonstration app
Process types are configured in your app’s Procfile
, so we want a single command that launches both statsd
and whatever the main work of this Dyno is going to be. Let’s start by making a simple Flask app and deploying it to Heroku without statsd
.
# requirements.txt
Flask==0.9
gunicorn==0.17.2
# web.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
And a simple Procfile
to launch that:
# Procfile
web: gunicorn -b "0.0.0.0:$PORT" -w 4 web:app
If we turn this into a git repo, create a Heroku app and push everything up, we’ll be able to see our very boring homepage.
$ git init
$ git add requirements.txt Procfile web.py
$ git commit -a -m 'Simple Flask app for Heroku.'
$ heroku apps:create
Creating afternoon-reaches-9313... done, stack is cedar
http://afternoon-reaches-9313.herokuapp.com/ | git@heroku.com:afternoon-reaches-9313.git
Git remote heroku added
$ git push heroku master
(Lots of unimportant output removed; the important bit is the output from heroku apps:create
which tells you the URL.)
Okay, all is well there. Let’s get statsd
into play.
Doing two things at once in a Dyno
The key here is to put a command in the Procfile
which launches both gunicorn
and the statsd
. A simple choice here is honcho
, which is a python version of foreman
. (If we were doing this using the Heroku Ruby runtime (say a Rails or Sinatra app) then it would make sense to use foreman instead.)
As we’re working in the python side of things, let’s add a simple statsd
counter to our web app at the same time.
# requirements.txt
Flask==0.9
gunicorn==0.17.2
honcho==0.4.0
python-statsd==1.5.8
# web.py
import statsd
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
counter = statsd.Counter("Homepage hits")
counter += 1
return "Hello World!"
if __name__ == "__main__":
app.run()
Honcho uses a Procfile
itself to figure out what to launch, so we need to give it separate configuration from the main Heroku one:
# Procfile.chain
web: gunicorn -b "0.0.0.0:$PORT" -w 4 web:app
statsd: cat /dev/zero
At this point we don’t know how to launch a statsd
so we’ll just have it launch a dummy command that will keep running while gunicorn
does its work. Then we need the main Heroku Procfile
to launch honcho
instead of gunicorn
directly:
# Procfile
web: USER=nobody PORT=$PORT honcho -f Procfile.chain start
(The USER
environment variable is needed because of how honcho
defaults some of its options.)
And push it to Heroku:
$ git add requirements.txt Procfile Procfile.chain web.py
$ git commit -a -m 'Run gunicorn + dummy process; python will try to push to statsd'
$ git push heroku master
The python that tries to push a counter to statsd
will fail silently if there isn’t one running, so all is well and you should still be able to get to your homepage at whichever URL Heroku gave you when you created the app.
Running statsd on Heroku
statsd
is a node.js
program, so we want the Heroku node.js
support in order to run it. Heroku supports different languages using buildpacks – and we’re already using the Python buildpack to run Flask. Fortunately there are community-contributed buildpacks available, one of which suits our needs: heroku-buildpack-multi allows using multiple buildpacks at once. We need to set this as the buildpack for our app:
$ heroku config:add BUILDPACK_URL=https://github.com/ddollar/heroku-buildpack-multi.git
Then we can add a .buildpacks
file that lists all the buildpacks we want to use.
http://github.com/heroku/heroku-buildpack-nodejs.git
http://github.com/heroku/heroku-buildpack-python.git
The node.js
buildpack uses package.json
to declare dependencies:
/* package.json */
{
"name": "heroku-statsd",
"version": "0.0.1",
"dependencies": {
"statsd": "0.6.0"
},
"engines": {
"node": "0.10.x",
"npm": "1.2.x"
}
}
statsd
itself needs a tiny amount of configuration; at this point we’re not going to consider an upstream, so we want it to log every message it gets sent so we can see it in the Heroku logs:
/* statsd-config.js */
{
dumpMessages: true
}
And finally we want to chain Procfile.chain
so honcho
knows to launch statsd:
web: gunicorn -b "0.0.0.0:$PORT" -w 4 web:app
statsd: node node_modules/statsd/stats.js statsd-config.js
Push that up to Heroku:
$ git add .buildpacks package.json statsd-config.js Procfile.chain
$ git commit -a -m 'Run statsd alongside gunicorn'
$ git push heroku master
If you hit your Heroku app’s URL you won’t see anything different, but when you check your Heroku logs:
$ heroku logs
2013-04-17T14:06:38.766960+00:00 heroku[router]: at=info method=GET path=/ host=afternoon-reaches-9313.herokuapp.com fwd="149.241.66.93" dyno=web.1 connect=2ms service=5ms status=200 bytes=12
2013-04-17T14:06:38.780056+00:00 app[web.1]: 14:06:38 statsd.1 | 17 Apr 14:06:38 - DEBUG: Homepage hits:1|c
Again I’ve removed a lot of boring output to focus on the two important lines: the first (from the Heroku routing layer; gunicorn
itself doesn’t log by default) shows the request being successfully processed, and the second shows statsd
getting our counter.
Pushing upstream
Both Librato and Hosted Graphite provide statsd
backends so you can aggregate directly to them. For Librato the plugin is statsd-librato-backend
, and for Hosted Graphite it’s statsd-hostedgraphite-backend
. Other options will either have their own backends, or you can always write your own.
As well any configuration to support your chosen upstream, you probably want to drop the dumpMessages: true
line so your Heroku logs are tidier.
Running locally
Everything we’ve done here will work locally as well. Assuming you have node.js
(and npm
) installed already, and you have virtualenv
on your system for managing python virtual environments, just do:
$ virtualenv ENV
$ source ENV/bin/activate
$ ENV/bin/pip install -r requirements.txt
$ npm install
$ honcho -f Procfile.chain start
Caveats
I haven’t used this in production (yet), so beyond the concept being sound I can’t commit to its working without problems. In particular, things to think about include:
honcho
isn’t usually used in production, so may have gotchas (note that if any process running under honcho
quits the entire thing will shut down, which means the Dyno will die and be replaced; this is almost certainly what you want)
- I don’t know how Dyno teardown works, and so statsd may lose data on Dyno cycle (which is rarely a huge problem)
- Not actually tested on more than one dyno at once
Certainly if you put this into production I’d pay attention to Heroku platform errors, do spot checks on data coming out of statsd
if you can, and generally be cautious.
- Published at
- Monday 24th December, 2012
(Throughout Advent I’m sharing some hints as to how web developers can make my life as a speech recognition user easier.)
It’s still advent, but lots of people have already started their trips to wherever they’re spending Christmas, so I just wanted to point out that a lot of the the normal accessibility advice helps voice users too. Nuance, who make Dragon, have guidelines for speech-accessible HTML which are worth looking at, even though they’re a few years old now, and based on the Windows version which has more features than the Mac version.
For a view of Dragon from the point of view of a web developer who just wanted to learn a little about Dragon and using it, check out Jon Whiting’s article from last year, Assistive Technology Experiment: Dragon NaturallySpeaking (he links to the same guidelines as above, although curiously under a different URL).
I’d recommend all web developers spend some time using either the Windows or Mac version; although it’s a significant amount of money to spend, it’s cheaper than some assistive technologies such as JAWS. If you’d like to have me come and talk to your company about using computers by voice, then please get in touch. I can of course include a demonstration (swearing as things go wrong strictly optional; please express preference at time of booking ;-), and if desired can perform a review in advance of a web site or app you’ve built, which can drive both demonstration and discussion.
- Published at
- Friday 21st December, 2012
(Throughout Advent I’m sharing some hints as to how web developers can make my life as a speech recognition user easier.)
Earlier this month I wrote about paging, suggesting that if you want floated information around your main content, the main content should scroll separately to the rest of your webpage. It turns out there’s a problem with this, although it is fixable. For a demonstration of what can go wrong, we turn to Quartz.
Looking at a Quartz story, there are three different things that might be scrollable – a list of topics that could scroll horizontally at the top, a list of stories that could scroll vertically down the left hand side and the story itself, on the right. (As I look at it now the main content is preceded in its scrollable area by an advert that takes up almost all of my screen. If I looked at this on a netbook, I wouldn’t have seen any content at all.)
You could, perhaps, make an argument for scrolling the stories list by default. Certainly there’s a strong argument for strolling the content itself by default. I think the topics list is fine as it is. There is no argument on earth that justifies what actually happens, which is that nothing scrolls by default.
What I mean is that pressing page up and page down, the keyboard access to scrolling (and, as it happens, the voice access as well), does absolutely nothing. Why not? Because the wrong thing has focus: I’m guessing the entire page (which is the default), but since that’s a fixed size filling the window, it won’t scroll. A simple touch of JavaScript will focus the correct scrollable area, and make life easier.
So really this is about focus, in which case I’ll take advantage of the opportunity to point out something nice I noticed about Barclays’ online banking system today. They have a fairly modern web app style, meaning that a lot of operations bring up a modal overlay to get them done. So making payments, transferring money and so on all have these little in-page dialogs. Not only do they support the escape key, they go one further when opening the overlays to make sure that focus is transferred to the first element of that overlay. This means you can tab through the controls on the overlay without worrying about what was behind it – avoiding a major annoyance in having overlays. They also have a number of other subtle features, such as showing jump links on focus.
- Published at
- Thursday 20th December, 2012
(Throughout Advent I’m sharing some hints as to how web developers can make my life as a speech recognition user easier.)
I wrote yesterday about enhancing long drop-down menus to turn them into combo boxes, which act more like text areas and so are somewhat more tractable to voice. However you can still screw them up; here are two ways I’ve seen recently.
The first is where you’re implementing auto complete on a text area. The best way of doing this is to provide a drop-down menu of possible completions and only fill the text area when one is explicitly selected. (This is how Google Search does it, for instance.) This means that until I finished dictating into the text area I can continue to use Dragon commands to correct that dictation. If you remember, Dragon maintains an internal view of what the text is in the current input field, so if you complete automatically in the text field this internal view is now incorrect. We might have the following, having dictated “James":
James Aylett
“James” has been input, but the web app has added “Aylett”, and the text cursor will be after that.
If I actually meant “chains”, and I use Dragon’s correction commands, Dragon will try to correct the word immediately before the text cursor, which it thinks is “James”, but which is actually “Aylett”. Dragon typically uses character by character selection, so what we are likely to end up with is something like “James Achains”.
Note that once the user has selected a completion from the menu, the text input is naturally going to contain some stuff that Dragon doesn’t know about. Voice users should be able to spot these kind of explicit situations, and have a command specifically to resynchronize Dragon’s view of an input, if they need to edit it further.
The other problem is more insidious, although I haven’t seen it in a web app as yet. It’s the way Google Chrome makes its address bar work. Someone on the Chrome team clearly decided that the “http://” part of the URL wasn’t necessary; other schemes are shown, so why take up space showing the most common? Except when you cut and paste the URL from Chrome it always includes the scheme, even if it’s HTTP. This is very clever, but trips up Dragon.
If you want to synchronize Dragon’s view of an input with what’s already there, you say “cache document”. It then selects all the text, and copies it into its own view; then it manually moves the cursor to the beginning then forward to the place where Dragon believes it to be. At this point (usually) Dragon and the input match each other, and voice editing commands will work smoothly.
But when the URL is copied out of Chrome’s address bar, the “http://” part is added to the front, meaning that Dragon thinks it’s there but Chrome, when editing commands are applied to the input field, does not. This creates a similar problem to the first example, in that trying to select parts of the text (to replace it, to add more before or after it, or to apply formatting commands such as capitalization) will select the wrong characters.
So with Google’s URL in the address bar (Google actually uses HTTPS for everything these days, but it’s easier to pretend it doesn’t for the purposes of explanation than to find a website that won’t move to HTTPS in the future), and Dragon thinking it’s synchronized, we might choose to go instead to Microsoft’s website, saying “select Google / Microsoft”. Dragon’s internal view is now “http://Microsoft.com”. However it will attempt to make that edit by selecting the eighth to thirteenth characters and typing “Microsoft” over them. Because the scheme isn’t in the actual text input, you end up with: “google.Microsoft”.
The only way of working round this is to copy the contents of the address bar out, edit them separately from Google Chrome, and then copy them back in again.
- Published at
- Wednesday 19th December, 2012
(Throughout Advent I’m sharing some hints as to how web developers can make my life as a speech recognition user easier.)
A simple one today: if you have long drop downs on any of your forms, such as if you have to ask for my country, then please use progressive enhancement to turn it into a combo box.
The problem is that your ordering of items within a drop-down is going to be wrong. You can do clever tricks – look up my likely country using geo-IP, then pull the most likely other options to the top before dropping to alphabetic order (although note that if you provide too many options out of order you make it harder for people to find the correct entry) – but anytime you have a large number of options in a list someone is going to get the short straw and have considerable difficulty.
For me this usually happens because “United Kingdom” comes in alphabetic order after “United Arab Emirates”, but people sometimes instead list it incorrectly as “Great Britain”. So I might select the drop-down (which usually does not open it), and say “United”, which will either select “United Kingdom” (if it’s pulled out towards the top of the list) or “United Arab Emirates” (if it’s not). If the latter, saying “Kingdom” will probably end up with the right option being selected, although if it’s in the list as “Great Britain” then all bets are off.
Worse, if “United Kingdom” is in the list and was selected to start off with but I didn’t notice, saying “United Kingdom” can end up with either “Iran” or “Kiribati” being selected (basically down to how much space I leave between the two words, although sometimes it actually selects the correct option). And if Dragon misrecognizes what I say, anything could be selected. My easiest choice at that point is to force the drop-down to open if it hasn’t already (by saying something like “down arrow") and trying to figure out by eye what the correct option is, hoping it’s actually on screen.
With a combo box, I can just say “United Kingdom” and, if Dragon misrecognizes my speech, I can use normal speech correction commands to make things right. Validation against the list should come as late as possible, to avoid messing with this process.
- Published at
- Tuesday 18th December, 2012
(Throughout Advent I’m sharing some hints as to how web developers can make my life as a speech recognition user easier.)
I wrote previously about being wary of the blur event; this hint is kind of the opposite. When you’re binding keystroke event handlers, be very careful about where you bind them to. This, at least, is what I think is happening with Google Instant.
If you have a reasonably modern computer you’ll see Instant working: on a Google results page if you type changes to your search and Google is confident that the results match what you’re looking for then the results will be updated “instantly”.
I have to turn it off. (All credit to Google for making this possible.)
The problem is that Instant interacts poorly with Glee Box; if I bring up Glee Box (typically by pressing g
) then anything I “type” into it also gets added to the Google search box. Then Instant kicks in a couple of seconds later and completely changes the search results (it also usually removes the Glee Box, for some reason).
Probably because Google uses the Google Web Toolkit extensively, it’s a bit too difficult to dig into it and figure out what’s actually going on here. But what I think is happening is that keystrokes are being captured by Google globally, probably using a handler on the document
object. These are then manually being added to the search box, even if they were typed into something else. I may be completely misrepresenting what is going on, because that’s frankly insane.
There are situations where you want a keystroke handler bound to the document
object; global keystrokes can be used to implement keyboard shortcuts in a web application, for instance. Google needs this on the search results page to implement keyboard navigation for search results. (Arrow keys moving through the different matches, for instance.) However when you want to do something clever with text typed into a specific box, you should be careful to make your handler only respond to keystrokes for the input element. (jQuery has a way of binding a keystroke handler to one element, but having it only respond to keystrokes from child elements matching a selector, which may help here.) Alternatively, you may be able to use a change
event on the input element instead.
I’m not really convinced that Google is doing what I’ve outlined above; for instance, there seems to be a keyboard handler on the input element as well as on the document
object. But whether they are or not, you should definitely be careful if building something similar.
- Published at
- Monday 17th December, 2012
(Throughout Advent I’m sharing some hints as to how web developers can make my life as a speech recognition user easier.)
If you want to be the opposite of helpful, why not try to be really clever and invent a completely new paradigm for a multistage process? Say, instead of using webpages, why not successively overlay different “form sheets” over the top of your actual webpage, so things like keyboard navigation through form elements have to go through everything in the original page before they get to the form you want someone to fill out?
If you do, you can enter the hallowed company of the Royal Museums Greenwich, where the only nice thing I can say about the experience is that pressing Escape closes the overlay.
There’s a nice “book now!” link (with correct alt text, meaning tools like Glee Box can find it), but instead of going to a new page it JavaScripts up an unsightly overlay. At this point it requires you to click a couple of links, and again Glee Box can manage this – but then you move to the second page:
In Safari, Dragon has commands to move between only input fields ("next field” and “previous field"), which it does not have for Chrome (I believe because Safari provides a richer set of options via AppleScript). However this doesn’t help here, because behind the overlay there are still a load of input fields: a search box, and then separately a number of controls for filtering events by date. You have to advance through all of these before you get to the form controls you actually care about. (It’s even worse in Chrome, where you can only advance by tabbing; either you have to change the system setting so that tab advances only through form fields, or you just take even longer as you navigate through all the links on the page as well.)
This is basically a tabbing order issue. You could either set it explicitly, or you could just add your overlay at the beginning of the HTML document; you’re loading the content in it dynamically and then floating it over the top of everything else anyway, so I’m not convinced there’s a downside. Or, of course, you could just build separate pages. Unless your backend system is utterly messed up it should be pretty much as fast anyway, and you won’t have to bother creating or stealing a “loading” spinner – the browser has one already nicely built into it.
- Published at
- Friday 14th December, 2012
(Throughout Advent I’m sharing some hints as to how web developers can make my life as a speech recognition user easier.)
Lots of sites provide a “jump to content” link designed for screen readers and the like; there often may be other jump links, to navigation, user tools and so on. Most people will then hide the links visually, typically by positioning them off the viewport, say using { position: absolute; left: -9999px; }
.
A couple of sites go further, and show the links again when they are focused. Here that is on Mother Jones; hit tab a couple of times and watch the top right corner.
Here’s why I like it: although you can see the focused anchor target in the status bar in most browsers, it may not be visible depending on configuration, and more importantly you can style it to fit in with your site. Some people won’t look at the status bar at all, and most people are going to be more attentive to your content – within the viewport – than to the browser’s chrome.
The way Mother Jones uses can be implemented in CSS, but we can go further with a tiny bit of JavaScript and show the entire set of jump links when any one is focused, using a single line of jQuery or slightly more pure JavaScript. Then you can do more sophisticated styling, including pushing the entire site content down, which sounds like it goes against yesterday’s advice but which I’d be okay with because tabbing is a keyboard operation, so you’re not going to confuse someone in the middle of a mouse operation by doing this. It’s also obvious, unmissable – and that’s helpful.
Why does this matter? I tend to use Glee Box to navigate links by voice, but sometimes it doesn’t work, or there are too many links with the same anchor text, or I want to read a long article opening links as I go. Making it easy to skip focus past your header and navigation is just as useful for me as for the other potential audiences of those jump links.
Dragon Naturally Speaking on Windows allows you to speak voice links natively, so you don’t require a plug-in like Glee Box; however the same issues can arise, particularly many links on a page (Dragon will only respond to a certain number, to avoid taking too long and getting in the user’s way) and the desire to open links while reading long articles.