Skip to content
Permalink
Browse files

Updated retainer task interface to correctly load assignments.

  • Loading branch information...
thisisdhaas committed Jun 19, 2015
1 parent 01bb3f5 commit 8921224c803522d7dfb8c9148ff42a2719e11c66
@@ -1,48 +1,59 @@
// Adapted from https://github.com/uid/realtime-turk

PING_INTERVAL = 2500;
WORK_INTERVAL = 1000;
PING_ENDPOINT = null;
WORK_ENDPOINT = null;

var Retainer = {
aid: null,
wid: null,
hid: null,
ping_type: 'waiting',

init: function(worker_id, assignment_id, task_id){
Retainer.aid = assignment_id
Retainer.wid = worker_id
Retainer.tid = task_id

Retainer.ping(worker_id, assignment_id, task_id, Retainer.ping_type)
Retainer.checkForWork(assignment_id)
},

ping: function(worker_id, assignment_id, task_id, ping_type){
$.post(PING_ENDPOINT +
'worker/' + worker_id + '/task/' + task_id + '/event/' + ping_type,
function(data, status){
console.log('pong', data)
setTimeout(Retainer.ping, PING_INTERVAL, worker_id, assignment_id, task_id, Retainer.ping_type)
}
)
},

checkForWork: function(assignment_id){
$.ajax({
url: WORK_ENDPOINT + 'assignment/' + assignment_id,
success: function(data, status){
if(data.start === true){
Retainer.ping_type = 'working'
Retainer.hasWork(data)
} else {
setTimeout(Retainer.checkForWork, WORK_INTERVAL, assignment_id)
}
console.log(data)
},
dataType: 'json'
})
},

hasWork: function(data){
console.log('initialize task here')
alert('start now')
}
requestData: null,

init: function(ping_url, work_url){
PING_ENDPOINT = ping_url;
WORK_ENDPOINT = work_url;
Retainer.requestData = prepare_submit_data();
Retainer.requestData.ping_type = 'waiting';
Retainer.ping(Retainer.requestData);
Retainer.checkForWork(Retainer.requestData);
},

ping: function(requestData){
$.post(PING_ENDPOINT,
requestData,
function(data, status){
console.log('pong', data)
setTimeout(Retainer.ping, PING_INTERVAL, requestData)
}
);
},

checkForWork: function(requestData){
$.get(WORK_ENDPOINT,
requestData,
function(data, status){
if(data.start === true){
Retainer.requestData.ping_type = 'working';
Retainer.hasWork(data);
} else {
setTimeout(Retainer.checkForWork, WORK_INTERVAL, requestData);
}
console.log(data);
},
'json'
);
},

hasWork: function(data){
console.log('initialize task here');
alert('start now');

task_frame = $('#taskFrame');
task_frame.attr('src', data.task_url);
task_frame.load(function() {
task_frame.show();
task_frame.height(task_frame.contents().height());
$('#waitingDiv').hide();
});

}
}
@@ -165,6 +165,7 @@
{% endblock misc_head_tags %}
</head>

{% block body %}
<body>
{% block form_tag %}
<form method="post" id="submitForm" action="">
@@ -245,4 +246,5 @@
{% block inline_js %}
{% endblock inline_js %}
</body>
{% endblock body %}
</html>
@@ -8,38 +8,82 @@
-->

<!-- Load the retainer library -->
{% block jslibraries %}
{{ block.super }}
<script type='text/javascript' src='/static/basecrowd/js/retainer.js'></script>
{% endblock jslibraries %}

{% block customjs %}
{{ block.super }}
{% endblock customjs %}
<!-- Don't include javascript we don't need. -->
{% block validate_func %}
{% endblock validate_func %}

{% block ready_func %}
{{ block.super }}
{% block initialize_form_validation_func %}
{% endblock initialize_form_validation_func %}

// Initialize the retainer object
PING_ENDPOINT = "/crowds/{{ crowd_name }}/retainer/ping/"
WORK_ENDPOINT = "todo"
PING_INTERVAL = 2500
WORK_INTERVAL = 1000
<!-- Initialization -->
{% block handle_is_accepted_func %}
function handle_is_accepted()
{
var is_accepted = {{ is_accepted|yesno:"true,false" }};
if (is_accepted)
{
// Set up retainer trackin
PING_ENDPOINT = "/crowds/{{ crowd_name }}/retainer/ping/";
WORK_ENDPOINT = "/crowds/{{ crowd_name }}/assignments/retainer/";
Retainer.init(PING_ENDPOINT, WORK_ENDPOINT);
}
}
{% endblock handle_is_accepted_func %}

{% block ready_func %}
$(document).ready(function()
{
var context = prepare_submit_data()
Retainer.init(context.workerId, context.assignmentId, context.HITId)
handle_is_accepted();
});
{% endblock ready_func %}

{% block default_instruction %}
<p> This will be a retainer task--we will show you various tasks inside this!</p>
{% endblock %}
<!-- Layout and Content -->


<!-- Override standard layout -->
{% block body %}

<!-- Instruction panel -->
<section class="container task-container">
<div class="row">
<div class="col-xs-12 col-md-12">
<div class="panel panel-primary">
<div class="panel-heading"><strong class="lead">Retainer Pool</strong></div>
<div class="panel-body">
<p> This will be a retainer task--we will show you various tasks inside this!</p>
<p> Talk about payment here?</p>
</div>
</div>
</div>
</div>

<div class="row">

<!-- Looking for work -->
<div class="well col-xs-12 col-md-12" id="waitingDiv">
<h1>Waiting for next task...</h1>
<p><big><em>Please keep this page open until the task begins. To earn your pay for waiting, you must be available as soon as you are alerted. Depending on your browser you will be alerted in different ways, but keep an eye out for a pop-up or flashing tab in your browser. Since you will be able to see the alert from other tabs, you are free to take other tasks while you wait, <b>as long as you do NOT close this tab</b>, as that will remove you from the waiting area.</em></big></p>
</div>

{% block item_data %}
<p>Hopefully unnecessary</p>
{% endblock %}
<!-- Task iframe, hidden to start -->
<div class="col-xs-12 col-md-12">
<iframe src="about:blank" id="taskFrame" frameborder="0" style="display:none;width:100%;"></iframe>
</div>

{% block item_labels %}
<p>Hopefully really unnecessary</p>
{% endblock %}
<!-- Button to stop working -->
{% if is_accepted %}
<div class="col-xs-12 col-md-12">
<p class="text-center">
<button id="exitButton" class="btn btn-primary">Finish working</button>
</p>
</div>
{% endif %}
</div>
</section>
{% endblock body %}
@@ -5,9 +5,12 @@
urlpatterns = patterns(
'',
url(r'^(\w+)/assignments/$', views.get_assignment, name='get_assignment'),
url(r'^(\w+)/assignments/retainer/$', views.assign_retainer_task,
name='assign_retainer_task'),
url(r'^(?P<crowd_name>\w+)/assignments/worker/(?P<worker_id>\w+)/task/(?P<task_id>\w+)$',
views.get_retainer_assignment, name='get_retainer_assignment'),
url(r'^(\w+)/responses/$', views.post_response, name='post_response'),
url(r'^(\w+)/tasks/$', views.create_task_group, name='create_tasks'),
url(r'^(\w+)/purge_tasks/$', views.purge_tasks, name='purge_tasks'),
url(r'^(?P<crowd_name>\w+)/retainer/ping/worker/(?P<worker_id>.+)/task/(?P<task_id>.+)/event/(?P<ping_type>.+)$',
views.ping, name='ping'),
url(r'^(?P<crowd_name>\w+)/retainer/ping/$', views.ping, name='ping'),
)
@@ -1,3 +1,5 @@
from django.core.urlresolvers import reverse
from django.db.models import Count, F
from django.template import RequestContext, TemplateDoesNotExist
from django.template.loader import get_template, select_template
from django.utils import timezone
@@ -6,10 +8,12 @@
from django.views.decorators.http import require_GET, require_POST
from django.http import HttpResponse
from datetime import datetime
from base64 import b64encode
import pytz
import json
import os
import logging
import uuid

from basecrowd.interface import CrowdRegistry
from basecrowd.models import TaskGroupRetainerStatus
@@ -115,6 +119,8 @@ def create_task_group(request, crowd_name):
if retainer_pool.status in [RetainerPoolStatus.IDLE,
RetainerPoolStatus.ACTIVE]:
current_group.retainer_pool_status = TaskGroupRetainerStatus.RUNNING
else:
current_group.retainer_pool_status = TaskGroupRetainerStatus.WAITING
current_group.save()

else: # Not retainer, create a task for each batch of points.
@@ -169,6 +175,7 @@ def purge_tasks(request, crowd_name):
def get_assignment(request, crowd_name):
# get the interface implementation from the crowd name.
interface, model_spec = CrowdRegistry.get_registry_entry(crowd_name)
logger.info('Non-retainer worker requested task assignment.')

# get assignment context
context = interface.get_assignment_context(request)
@@ -182,6 +189,10 @@ def get_assignment(request, crowd_name):
template = get_scoped_template(crowd_name, 'unavailable.html')
return HttpResponse(template.render(RequestContext(request, {})))

return _get_assignment(request, crowd_name, interface, model_spec, context)


def _get_assignment(request, crowd_name, interface, model_spec, context):
# Retrieve the task based on task_id from the database
try:
current_task = model_spec.task_model.objects.get(
@@ -291,19 +302,109 @@ def post_response(request, crowd_name):

return HttpResponse('ok') # AJAX call succeded.


# Views related to Retainer Pool tasks
#######################################


@require_POST
@csrf_exempt
def ping(request, crowd_name, worker_id, task_id, ping_type):
def ping(request, crowd_name):
interface, model_spec = CrowdRegistry.get_registry_entry(crowd_name)
task = model_spec.task_model.objects.get(task_id=task_id)
worker = model_spec.worker_model.objects.get(worker_id=worker_id)

# get and validate context
context = interface.get_response_context(request)
interface.require_context(
context, ['task_id', 'worker_id'],
ValueError("ping context missing required keys."))
task = model_spec.task_model.objects.get(task_id=context['task_id'])
worker = model_spec.worker_model.objects.get(worker_id=context['worker_id'])

# TODO: track ping type?
# ping_type = request.POST['ping_type']

# TODO: make this not broken when a worker is in multiple pools
worker.last_ping = timezone.now()
worker.save()
logger.info('ping from worker %s, task %s' % (worker, task))

return HttpResponse('ok')


@require_GET
def assign_retainer_task(request, crowd_name):
# get the interface implementation from the crowd name.
interface, model_spec = CrowdRegistry.get_registry_entry(crowd_name)

logger.info('Retainer task requested work.')
context = interface.get_response_context(request)
interface.require_context(
context, ['task_id', 'worker_id'],
ValueError("get_assignment context missing required keys."))
task = model_spec.task_model.objects.get(task_id=context['task_id'])
worker = model_spec.worker_model.objects.get(worker_id=context['worker_id'])
pool = task.group.retainer_pool

# Look for a task the worker is already assigned to
assignment_task = None
existing_assignments = (worker.tasks
.filter(group__retainer_pool=pool)
.exclude(task_type='retainer'))
if existing_assignments.exists():
assignment_task = existing_assignments[0]
else: # Look for open tasks
open_tasks = (

# incomplete tasks
model_spec.task_model.objects.filter(is_complete=False)

# in this pool's tasks
.filter(group__in=pool.task_groups.all())

# that aren't dummy retainer tasks
.exclude(task_type='retainer')

# that the worker hasn't worked on already
.exclude(responses__worker=worker)

# that haven't been assigned to enough workers yet
.annotate(num_workers=Count('workers'))
.filter(num_workers__lt=F('num_assignments')))

# Pick a random one and assign it to the worker
if open_tasks.exists():
assignment_task = open_tasks.order_by('?')[0]
worker.tasks.add(assignment_task)

# return a url to the assignment
if assignment_task:
url_args = {
'crowd_name': crowd_name,
'worker_id': worker.worker_id,
'task_id': assignment_task.task_id,
}
response_data = json.dumps({
'start': True,
'task_url': reverse('basecrowd:get_retainer_assignment',
kwargs=url_args)
})
return HttpResponse(response_data)

# we need this view to load in AMT's iframe, so disable Django's built-in
# clickjacking protection.
@xframe_options_exempt
@require_GET
def get_retainer_assignment(request, crowd_name, worker_id, task_id):
# get the interface implementation from the crowd name.
interface, model_spec = CrowdRegistry.get_registry_entry(crowd_name)
logger.info('Retainer worker fetched task assignment.')

# construct assignment id
context = {
'task_id': task_id,
'worker_id': worker_id,
'is_accepted': True,
'assignment_id': uuid.uuid4()
}

return _get_assignment(request, crowd_name, interface, model_spec, context)

0 comments on commit 8921224

Please sign in to comment.
You can’t perform that action at this time.