Monday, 26 March 2012

Exam Prepration jQuery Tool

I do love 'er-indoors but she's not the most patient of people. Sure, she manages to actively listen for a while but ask her to do something that doesn't interest her and the result is obvious boredom. Which is a shame as I needed her help to revise for my DEV 401 exam.

I asked if I could borrow her for an hour or two on Saturday and Sunday. "Sure", says she in a nice surrendered wife type of way. My idea was to go through possible questions and give answers and ask her to say if I'd got it right or wrong. She agreed and we sat down but the delay of a few micro-seconds before I got a reply when I asked her a question was doing my nut in. I looked down to see her farming Smurfs or something. I really couldn't face putting her, or myself, through it all again the next day.

jQuery to the rescue!

I copied the questions I had into an array of JSON objects with the questions, the possible answers and the solution. This worked a treat as I could swap over questions easily enough and re-task it for other purposes.

I thought about trying to pluginify it but I don't really think it needs it as it takes a set of questions, answers and solutions and spits back a set of divs into the container of your choice. Props to David for the compare function.

(function(){
    prepareExam("chap1.json", "body");
    $("select#paper").on("change", function(){
        $val = $(this).val();
        $("div.question").empty().remove();
        $("div.score").empty().remove();
        prepareExam($val, "body");                    });
})();
jQuery.fn.compare = function(t) {
    if (this.length != t.length) {         return false;     }
    var a = this.sort(),
        b = t.sort();
    for (var i = 0; t[i]; i++) {
        if (a[i] !== b[i]) {             return false;
        }
    }
    return true;
};
function prepareExam(paper, target) {
    $.getJSON(paper, function(exam) {
        $.each(exam.exam, function(i, question){
            var d = $("<div></div>").addClass("question").appendTo(target);
            var q = $("<p></p>").html(i+1+". "+question.question).appendTo(d);
            var f = $("<fieldset></fieldset>").appendTo(d);
            $.each(question.answers, function(letter, possible){
                var labelID = letter+(i+1);
                $("<input></input>", {"type":"checkbox", "name":i+1,"value":letter, "id":labelID}).appendTo(f);
                $("<label></label>", {"for":labelID}).text(letter+". "+possible).appendTo(f);
                f.append('<br />');
            });
            d.data({"answer":question.solution});
            var c = $("<span></span>").addClass("check").text("Submit").button().appendTo(f).on("click", function(){
                var $parent = $(this).parent().parent();
                var allVals = [];
                $.each($parent.find("input:checked"), function(x, y){
                    allVals.push($(y).attr("value"));
                })
                if($(allVals).compare($parent.data("answer"))){
                    $parent.css("background-color","#ccffcc");
                    $parent.find("span.check").remove();
                    $parent.data({"success":true});
                }else{
                    $parent.css("background-color","#ffcccc");
                    $parent.find("span.check").remove();
                    $parent.data({"success":false});
                }
            });
            var leg = (question.solution.length !== 1) ? "answers" : "answer";
            $("<legend></legend>").text("Please choose "+question.solution.length+" "+leg+":").prependTo(f);
        });
        var s = $("<div></div>").addClass("score").appendTo(target);
        $("<p></p>").text("Ready to calculate your final score?").appendTo(s);
        $("<span></span>").addClass("final").text("Check").button().appendTo(s).on("click", function(){
            var total = 0, correct = 0;
            $.each($("div.question"), function(i, div){
                total++;
                if($(div).data("success")){
                    correct++;
                }
            });
            var l = $("<p></p>").text("Total Score = "+(((correct/total)*100).toFixed(2))+"%").appendTo($("div.score"));
            if(((correct/total)*100).toFixed(2) >= 68){
                l.css("color","green")
            }else{
                l.css("color","red")
            }
            $("span.final").remove();
        });
    });
};

As you can probably tell I used a lot of different sets of questions and I added them to a select input so I could load up a set of new questions when I'd done a paper and scored myself.

The format of the JSON should be:

{
    "exam": [
        {
            "question": "Jack and Jill went up the hill to fetch?",
            "answers": {
                "a": "A pail of water",
                "b": "Porridge",
                "c": "Vinegar"
            },
            "solution": [
                "a"
            ]
        },
        {
            "question": "What did the old woman who lived in a shoe do to her children?",
            "answers": {
                "a": "She gave them some broth without any bread.",
                "b": "She starved them.",
                "c": "She sent them to sweep chimneys.",
                "d": "She whipped them all soundly and put them to bed."
            },
            "solution": [
                "a",
                "d"
            ]
        }
    ]
}

Have fun!

I think it's lovely so by all means take it, use it and let me know if you improve it, let me see where you use it too ehh?

This is now on github.