Skip to content

Creating a new task

mlent edited this page Nov 13, 2014 · 3 revisions

What is a task?

A task is essentially a learning exercise. Tasks are designed to be centered around words or phrases that appear in the textual data.


Basic structure of a task

Tasks consist of a javascript file in static/js/views/lessons/tasks/ and an underscore template in templates/js/lessons/tasks/.

Task States

When a user is interacting with a task, the task is in one of several states:

  • Open User has not yet interacted with the task, or has not yet submitted any final answers.
  • Success User has successfully completed the task.
  • Warning User has the task partially correct, and show be allowed to try again.
  • Error User is beyond all hope.

Textual Data

When creating your task in Javascript, you can expect to have access to:

  • this.model - the word you're working on. This is selected for your task based on the morphology of the unit. If the morphology described by the unit isn't appropriate for your task, your task isn't appropriate for the unit. Assign it to another unit or re-think your task.
  • this.collection - all the words we've pulled via the API so far. This will also allow you to access the word's sentence, other words in the current vocabulary set, and more.

For more information about what you can do with this data in your tasks, check out other tasks as examples or look in static/js/models/word.js and static/js/collections/words.js.


Creating a new task

Clone sample_task

All tasks inherit from the base_task.js view, which handles things like answer checking, sending submissions to the backend, evaluating how correct a user's answers are, etc. You should be aware of the content of base_task.js so you don't do something silly like override an important method.

Start by creating a copy of the file sample_task.js.

cd /opt/phaidra/static/js/views/lessons/tasks/
cp sample_task.js my_new_task_name.js

Your task name should be short and contain only lowercase letters, with words separated by underscores if needed.

The sample_task.js Skeleton, explained

I'll go through the parts of this file piecewise.

define(['jquery', 
    'underscore', 
    'backbone', 
    'views/lessons/tasks/base_task',
    'utils',
    'typegeek',
    'text!/templates/js/lessons/tasks/sample_task.html'], 
    function($, _, Backbone, BaseTaskView, Utils, TypeGeek, Template) {

Declare our dependencies, and pull in our template. Obviously, you need to change sample_task.html to whatever HTML template you want to use.

We're also pulling in typegeek to enable typing Greek letters. If you don't need this, you can remove this line from the dependency array, and in the list of function arguments.

return BaseTaskView.extend({
            template: _.template(Template),
            events: {
                'submit form': 'checkAnswer'
            },  
            initialize: function(options) {
                console.log(options.args);
                BaseTaskView.prototype.initialize.apply(this, [options]);
            },

Very standard if you're used to Backbone. Note that the initialize function in BaseTaskView takes care of fetching the sentence related to our word model, so make sure you always call the super function even if you do some extra stuff in here.

            // Initial render just to make $el available to parent view
            render: function() {
                return this;
            },  

            // Called when the sentence is populated
            fullRender: function(options) {
                this.$el.html(this.template({
                    model: this.model
                }));
    
                // Now that the DOM is available, bindings:
                var answerBox = this.$el.find('input[type="text"]')[0];
                new TypeGeek(answerBox);
                answerBox.focus();
            },

Again, very basic. We delay fullRender of the view until the data is fetched. And once we full-render, we populate the template with the model, initialize the TypeGeek plugin, and presto!

Note that here, your HTML template could be as basic as:

<p>Type the word <%= model.get('value') %></p>
<input type="text">

And it will prompt the user to type some selected greek word into this input field.

           checkAnswer: function(e) {
                e.preventDefault();

                // Grab answers from the UI, pass to base_task
                var inputField = $(e.target).find('input');
                var answer = this.model.get('value');
                var userAnswer = inputField.val();

                // Call our BaseTask's answer checking functionality 
                var newState = BaseTaskView.prototype.checkAnswer.apply(this, answer, userAnswer);

                // Update our UI accordingly
                this.fullRender({ state: newState });
            }
        });
    }
);

The comments really explain everything. Use the checkAnswer field to update the state that the task is in.

Once the view goes into a success state, the task manager will automatically set a TimeOut and switch to the next task. So only put users into a success state when they are ready to proceed. Otherwise, avoid changing the state (or keep it 'open').


Add this task via the Admin Panel

Create the task

Assign it to a task sequence

Assign this task sequence to a grammar topic


Advanced Task Creation

Passing arguments to the task

Say, for example, that you want to create a couple different flavors of the same task. For the sake of the example, let's consider a task called identify_morphology. It would be pretty damn tedious to create a different view for each aspect of morphology (think: identify_morph_number, identify_morph_tense, etc.).

Instead, you can pass a single string argument to the view, and then handle this within your javascript. E.g. Create a file identify_morph.js, and create tasks in the Admin Panel such as identify_morph:number, identify_morph:tense, etc. Anything after the : will be passed to your task view in the options.args, during initialize().

For example:

identify_morph.js

Backbone.View.extend({
   initialize: function(options) {
       console.log(options.args);   // will print 'number'

       // If you intend to override initialize, make sure to also always call super
       BaseTaskView.prototype.initialize.apply(this, [options]);
   }
});

Developer Guide

Backend

Frontend

Content Guide

What did you break?

  • Postgres Database
  • Neo4j
  • Django
  • "The server"
  • Git
  • UI
Clone this wiki locally