Monthly Archives: November 2009

Keywords, not experience, make the man

I had a chat with a recruitment agency today, the first useful one that I’ve had since I became unemployed. It was remarkably helpful, but also rather depressing. It was clear that she wasn’t actually looking at my CV, just searching for keywords and then expanding on them. The job was Unix system analyst. I have been a Solaris system administrator by trade for 15 years, and have worked with Linux for much of that time, but not so much professionally. My current CV has been trimmed for brevity: years as a contractor have seen to that as I have taken short jobs to fulfill functions and which means there’s nothing much to say about them. My actual paid Linux experience is ten years past and of course Linux has changed enormously since then.
It was clear that only Linux experience would do: 15 years of working in enterprise Solaris environments, including ones that matched the requirement, wasn’t enough. No doubt the client’s recruitment manager would look for the same keywords and make a decision in the same way. There was no suggestion that there would be any technical input before it presumably got to the Technical Manager.
The spec for this job was loose and didn’t really specify any skills different to any other Unix system administration role.
This is why I believe that the recruitment process doesn’t scale. When faced with 30 CVs for a post, an agent, who might be working from a vague requirement, can’t really do anything but grep keywords, so there’s no understanding of a candidate’s skills, just a bunch of boxes to tick. The client’s HR probably also doesn’t wholly understand the requirement, and applies the same criteria before presenting the filtered CVs to the person within the company who is making the final decision.
If every recruitment agency does that, then it comes as no surprise that those of us who might not have formal qualifications (a computing degree is easy to find), or with transferable skills that don’t include keywords that are exactly the same as what the agency is looking for, are struggling to get work. If I show my CV to technically oriented people they are impressed by my range of experience, but at the moment such people need cheap, and I can understand that, but it seems wrong that the process that I have made a living from in the good times fails in bad times. In my line of business that would be called a point of failure in a mission critical system. Recruitment agents would complain if their email systems failed, but their own system has failed and it’s affecting their candidates and clients.

Advertisements

The death of the recruitment agency

I’ve been technically unemployed for almost a year. I finished my last contract in December 2008 and haven’t been able to get a job since. This isn’t to say that I’ve spent the last year watching Jeremy Kyle in my pants, but that no-one seems to want to pay me for the work that I am, and let’s not mince words here, pretty bloody good at doing.
For a while I wanted to chart my own course, and try and find ways of doing things that don’t involve working for the man but circumstances changed very much for the better, and while they confirmed what I had been trying to do for a year or so, to at least stay in the same place and settle down, I’ve run up against something of a brick wall in regard to finding a job.
In fat times, the recruitment agent is your best mate. There were times when I could put my CV on Jobserve and then spend a few days fielding offers ranging from the ridiculous to the sublime. When I moved from London to Leeds in 2001, I secured a job within a month of making the decision to move.
Moving to contracting after a few years, when I got itchy feet, was easy enough too, but to cut a long story short, despite living in reach of two of the biggest (if not necessarily the most prosperous) cities in the country, the global financial collapse and the recession has put paid to much of the IT business in the area. I am, for the first time in fifteen years, signing on, and even considering retraining.
At this time the recruitment consultant isn’t your friend. He stops returning your calls. When he does call it’s for jobs that have no relevance to your experience or are 200 miles away. Even the jobs that are posted to the big job boards are untrustworthy, scraped from company websites, incoherently copied and pasted, poached from other agencies and just plain made up to harvest CVs to meet quotas. When it has been the only method of looking for work that one has known for most of your working career, it becomes difficult to change. However, the alternatives don’t seem to work, as employers seem not to have got that either.
So, if anyone out there is looking for an aging, yet very competent sysadmin somewhere along the M62, get in touch. Stop paying agencies to top slice the first five CVs they get before going back to playing Call of Duty. Post your own ads on the job boards and make your own decisions. You might be less disappointed.

Dynamic select fields with JQuery and django

I don’t post much about my experiences with django as I’m generally slowly learning and not really coming up with any revelations. However, this week, I’ve come up against a problem that has half an answer, but not a satisfactory one, and one that no-one on the django-users mailing list seems to have been able to resolve.

Your form has nested categories that depend on each other: a top category and a sub category, rather like eBay. Rather than reloading the whole page to get a list of subcategories when a top category is selected, it would look much nicer just to populate the subcategory select field. This is a job for javascript, specifically AJAX, and to save reinventing the wheel, it’s useful to use a library, which in my case is currently JQuery.

This blog post and this blog post set me off in the right direction by showing how to use JQuery’s getJSON function and how to craft custom fields for the lookup.

First, you’ll need to define a form. In this case I’m using a ModelForm with a couple of custom field definitions. This is probably slightly redundant as the field choices will be looked up by the ModelForm factory, but we are changing the definition of the prodsubcat field by disabling its widget, so I’ve also included the field choices for completeness:

class ProductForm(ModelForm):
    prodtopcat = forms.ModelChoiceField(ProductTopCategory.objects, widget=forms.Select)
    prodsubcat = forms.ModelChoiceField(ProductSubCategory.objects, widget=forms.Select(attrs={'disabled': 'true'}))

    class Meta:
        model = Product

Something I bashed my head against for a while was from Dustin’s post: setting the choices option on the widget as follows to display a message in the disabled subcategory field:

prodsubcat = forms.ModelChoiceField(ProductSubCategory.objects, widget=forms.Select(attrs={'disabled': 'true'}), choices=(('-1','Select Make'),))

which looks neat, but wipes out the choices dictionary that is loaded at render time. This is important as it’s that dictionary that the form submission process validates against. I got stuck on that for a day or so.

The javascript to make the JSON request sits in the SCRIPT section in the HEAD part of your template and is pretty simple. It also needs a current version of jQuery to be present:

  $(function(){
    $("select#id_prodtopcat").change(function(){
      $.getJSON("/products/feeds/subcat/"+$(this).val()+"/", function(j) {
        var options = '<option value="">---------- </option>';
        for (var i = 0; i < j.length; i++) {
          options += '<option value="' + parseInt(j[i].pk) + '">' + j[i].fields['longname'] + '</option>';
        }
        $("#id_prodsubcat").html(options);
        $("#id_prodsubcat option:first").attr('selected', 'selected');
        $("#id_prodsubcat").attr('disabled', false);
      })
      $("#id_prodtopcat").attr('selected', 'selected');
    })
  })

This calls a django view that returns a JSON object using HTTP GET. The view looks like this:

def feeds_subcat(request, cat_id):
	from django.core import serializers
	json_subcat = serializers.serialize("json", ProductSubCategory.objects.filter(prodcat = cat_id))
	return HttpResponse(json_subcat, mimetype="application/javascript")

This returns an object filtered on the ProductTopCategory relationship and passes it through the serialiser. The jQuery function(j) formats the JSON object as a HTML SELECT field and writes it to the form using JQuery’s document.write function.

You should now have a form that among other things has an active top category select field and a disabled sub category select field. Selecting an option in the top category field should enable the sub category and populate it with a filtered set of options.

Submit the form, and if you’re using the standard django method of submission and validation, it should pass and you can continue to process the form.

The thing that I got stuck on was the validation aspect: in retrospect it’s fairly obvious that the form will validate against the object that it has in memory, but I got sidetracked for a while in other solutions such as attempting to replace the submitted form data, which can’t be done as request.POST is read only (you could make a copy but that really just adds to the codebase), and also creating custom validation for the field that did a lookup for a record that matched the selected option, thus overriding validation of the rendered field.

It’s still a little bit hacky in that it needs two lookups but on the other hand the alternative is to load a potentially big directory structure into the browser memory in order to filter the choices.

Inline editing with Jquery, Jeditable and django

…and two come along at once

In attempting to keep my current project easy to use and pleasingly contemporary, I decided to get javascripty on the user dashboard by enabling editing in place, like all the best Web 2.x sites *cough*.

Inline editing is where a user can change aspects of a page by clicking on the data to be changed, editing in a form object and sending the changed data back to the server. As I have been using Jquery for most of my UI needs, I looked for a solution that used it and found jeditable, a plugin that does exactly what I want it to do, but doesn’t seem to have been documented for django as yet, so here’s a first crack at making it work.

My user dashboard is pretty straighforward from a django perspective and uses the standard AUTH_PROFILE module. It may have to inherit some additional models shortly but in the spirit of keeping it simple, I use django.contrib.auth and a user profile model called UserProfile.

Jeditable is simple enough in a Jquery kind of way: enclose the data that you want to be edited in a div with a class identifier and specify a URL that will process the edited data in the editable function.The demo code is all PHP, but it’s simple enough to translate that to django:

$(document).ready(function() {
     $('.edit').editable('/users/dashboard/edit/{{ user.username }}/', {
     	style: 'display: inline'
     });
});

My URL pattern is fairly easy to work out from that:

(r'^dashboard/edit/(?P<username>\w+)/$', 'edit_dashboard'),

In order to make it generic as possible, there should be one function for the editable area and one django view. This is the science bit. Jeditable sends a POST request which by default is formed thusly:

id=elements_id&value=user_edited_content

This isn’t explained that well on the Jeditable page in my opinion, but the author is Finnish so we’ll let him off. What it means is that the name of the div class is sent a value pair with ‘id’ and the edited data is sent as ‘value’. This is modifiable but it’s as good a default as any to use.

In keeping with django’s MVC structure, set the div element name (‘id’) to be the same as the associated model field. There’s probably a more dynamic way of doing this, such as defining the div element as the field name from the dictionary created by the view, but creating it by hand will be fine. Each div element therefore looks something like this:

<div class="edit" id="first_name">{{ user_obj.user.first_name }}</div>

Pass the field and its value to the view using request.POST.get():

            field = request.POST.get('id', '')
            value = request.POST.get('value', '')

This is where I got a bit stuck for a while. I have the field and I have the value, how do I pass them to the object to be saved? The answer was found in this post by James Bennett. A model, or object derived from a model has an API that calls all sorts of useful information about your model, and, more to the point can be invoked to modify that information in a generic way. The _meta functions are part of django’s internals, and as a comment in the post points out, might be subject to change, but we don’t need to the _meta functions, as the API call that we’re interested in, __setattr__ is also exposed directly to the model or object, so the field and value can be passed to the database like this:

user_obj.__setattr__(field, value)

and can be saved with a simple save() method. Try it in a shell.

UPDATE: this doesn’t work with the User.first_name and User.last_name fields. They can be accessed using

user_obj.user.__setattr__(field, value)

and saved with user_obj.user.save(). The first_name and last_name field in auth seem to be largely deprecated and so might be better off in UserProfile: it certainly saves a couple of lines of code.

Jeditable expects a response, which a PHP author would roll themselves. Fortunately, django has HttpResponse. I return the value variable, which is displayed as the edited data. The stored data will not be shown until the page is fully reloaded.

This example is pretty primitive really, and there are things that seem to be able to be improved. The database query is a simple user.get_profile() call but this request gets made for every item that is changed, which seems inefficient. It seems there should be something a bit more asynchronous about it , possibly using JSON or XML and making a periodic save rather than a query for every item. A bit of processing power could be saved if we checked if the data had changed before saving it. However, at the moment I’m a programmer with a deadline 😉

In which the adventure continues

My company, which none of you will remember, has had to fold for various reasons, so the website will be coming down in time, so it made sense to me (and probably no-one else) to migrate my blog to WordPress where someone might actually want to read it. In time I’ll repost some of the things from the old blog should they be of use to anyone. So read on…