-
Notifications
You must be signed in to change notification settings - Fork 6
Creating a new task
A task is essentially a learning exercise. Tasks are designed to be centered around words or phrases that appear in the textual data.
Tasks consist of a javascript file in static/js/views/lessons/tasks/
and an underscore template in templates/js/lessons/tasks/
.
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.
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
.
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.
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').
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 tools and workflow
- API
- Neo4j-powered
- Postgres-powered
- Data
- Internationalization
- General information
- Developer tools and workflow
- Project structure overview
- Frontend vs. Backend Templates
- Backbone.js app structure
- Internationalization for JS
- Guidelines for creating content
- Adding a locale to Phaidra
- Postgres Database
- Neo4j
- Django
- "The server"
- Git
- UI