From 57e771ce95931ac0d40a6fe00521b8f59f492fbb Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 8 Jun 2016 14:09:03 -0700 Subject: [PATCH 001/142] add new fields to battery model for battery prerequisite --- expdj/apps/experiments/models.py | 53 +++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index cae337e..4c7f9e6 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -1,14 +1,16 @@ +import collections +import operator + from guardian.shortcuts import assign_perm, get_users_with_perms, remove_perm -from django.core.validators import MaxValueValidator, MinValueValidator -from django.db.models.signals import m2m_changed +from jsonfield import JSONField from polymorphic.models import PolymorphicModel -from django.core.urlresolvers import reverse + from django.contrib.auth.models import User -from django.db.models import Q, DO_NOTHING -from jsonfield import JSONField +from django.core.urlresolvers import reverse +from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -import collections -import operator +from django.db.models import Q, DO_NOTHING +from django.db.models.signals import m2m_changed class CognitiveAtlasConcept(models.Model): name = models.CharField(max_length=1000, null=False, blank=False) @@ -133,6 +135,12 @@ def __str__(self): class Battery(models.Model): '''A battery is a collection of experiment templates''' + + ORDER_CHOICES = ( + ("random", "random"), + ("specified", "specified"), + ) + # Name must be unique because anonymous link is generated from hash name = models.CharField(max_length=200, unique = True, null=False, verbose_name="Name of battery") description = models.TextField(blank=True, null=True) @@ -150,19 +158,28 @@ class Battery(models.Model): active = models.BooleanField(choices=((False, 'Inactive'), (True, 'Active')), default=True,verbose_name="Active") - ORDER_CHOICES = ( - ("random", "random"), - ("specified", "specified"), - ) - presentation_order = models.CharField("order function for presentation of experiments",max_length=200,choices=ORDER_CHOICES,default="random",help_text="Select experiments randomly, or in a custom specified order.") - blacklist_active = models.BooleanField(choices=((False, 'Off'), - (True, 'On')), - default=False,verbose_name="Blacklist based on rejection criteria") + blacklist_active = models.BooleanField( + choices=((False, 'Off'), (True, 'On')), + default=False, + verbose_name="Blacklist based on rejection criteria" + ) blacklist_threshold = models.PositiveIntegerField(null=True,blank=True,default=10,help_text="Number of experiments to fail reject condition to add participant to blacklist",validators = [MinValueValidator(0.0)]) - bonus_active = models.BooleanField(choices=((False, 'Off'), - (True, 'On')), - default=False,verbose_name="Bonus based on reward criteria") + bonus_active = models.BooleanField( + choices=((False, 'Off'), (True, 'On')), + default=False, + verbose_name="Bonus based on reward criteria" + ) + required_batteries = models.ManyToManyField( + Battery, + help_text=("Batteries which must be completed for this battery to be " + "attempted") + ) + restricted_batteries = models.ManyToManyField( + Battery, + help_text=("Batteries that must not be completed in order for " + "this battery to be attempted") + ) def get_absolute_url(self): return_cid = self.id From 6e8a6df98d957af8ea1932b0b105ac6d800b087f Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 8 Jun 2016 16:50:21 -0700 Subject: [PATCH 002/142] updated many to many fields in battery to handle self reference properly --- expdj/apps/experiments/forms.py | 4 ++-- expdj/apps/experiments/models.py | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/expdj/apps/experiments/forms.py b/expdj/apps/experiments/forms.py index f72180a..fea9378 100644 --- a/expdj/apps/experiments/forms.py +++ b/expdj/apps/experiments/forms.py @@ -57,8 +57,8 @@ def __init__(self, *args, **kwargs): class BatteryForm(ModelForm): class Meta: - exclude = ('owner','contributors','experiments','bonus_active' - 'blacklist_active','blacklist_threshold') + exclude = ('owner', 'contributors', 'experiments',' bonus_active' + 'blacklist_active', 'blacklist_threshold') model = Battery def clean(self): diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index 4c7f9e6..611abd5 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -171,12 +171,16 @@ class Battery(models.Model): verbose_name="Bonus based on reward criteria" ) required_batteries = models.ManyToManyField( - Battery, + "Battery", + blank=True, + related_name='required_batteries_mtm', help_text=("Batteries which must be completed for this battery to be " "attempted") ) restricted_batteries = models.ManyToManyField( - Battery, + "Battery", + blank=True, + related_name='restricted_batteries_mtm', help_text=("Batteries that must not be completed in order for " "this battery to be attempted") ) From bb52ed33f199831e9f76d9ed6199b27ca680d530 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 8 Jun 2016 16:50:56 -0700 Subject: [PATCH 003/142] have restricted/required battery form fields use select2 --- .../experiments/templates/experiments/edit_battery.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/expdj/apps/experiments/templates/experiments/edit_battery.html b/expdj/apps/experiments/templates/experiments/edit_battery.html index 9acb059..b17f028 100644 --- a/expdj/apps/experiments/templates/experiments/edit_battery.html +++ b/expdj/apps/experiments/templates/experiments/edit_battery.html @@ -1,6 +1,8 @@ {% extends "main/base.html" %} +{% load static %} {% load crispy_forms_tags %} {% block head %} + {% endblock %} {% block content %} @@ -22,3 +24,10 @@

{{ header_text }}

{% endblock %} +{% block scripts %} + + +{% endblock %} From b6005b0c8fedc9e447c0910fd488d30203bd3686 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 9 Jun 2016 12:39:49 -0700 Subject: [PATCH 004/142] basic logic for checking battery requirements, untested --- expdj/apps/experiments/views.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index ce3f2a0..4629426 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -392,6 +392,29 @@ def serve_battery(request,bid,userid=None): if isinstance(worker,list): # no id means returning [] return render_to_response("turk/invalid_id_sorry.html") + worker_results = Result.objects.filter(worker=worker, completed=True) + worker_completed_batteries = [] + for result in worker_results: + worker_completed_batteries.append(result.battery) + + missing_batteries = [] + for required_battery in battery.required_battery_set.all(): + if required_battery not in worker_completed_batteries: + missing_batteries.append(required_battery) + + blocking_batteries = [] + for restricted_battery in battery.restricted_battery_set.all(): + if restricted_battery in worker_completed_batteries: + blocking_battery.append(required_battery) + + if missing_batteries or blocking_batteries: + return render_to_response( + "turk/battery_requirements_not_met.html", + context={'missing_batteries': missing_batteries, + 'blocking_batteries': blocking_batteries} + ) + + # Try to get some info about browser, language, etc. browser = "%s,%s" %(request.user_agent.browser.family,request.user_agent.browser.version_string) platform = "%s,%s" %(request.user_agent.os.family,request.user_agent.os.version_string) From 0fd2cce16e81042180f8a98255918f7ec94bc7e2 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Jun 2016 14:27:44 -0700 Subject: [PATCH 005/142] update how mtm relations are accessed when testing battery requirements. Changed template to be in experiments templates. --- expdj/apps/experiments/views.py | 87 +++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index 4629426..8f25fa1 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -1,43 +1,56 @@ -from django.shortcuts import get_object_or_404, render_to_response, render, redirect -from expdj.apps.experiments.models import ExperimentTemplate, Experiment, Battery, \ - ExperimentVariable, CreditCondition -from expdj.apps.experiments.forms import ExperimentForm, ExperimentTemplateForm, BatteryForm, \ - BlacklistForm -from expdj.apps.turk.utils import get_worker_experiments -from expdj.apps.turk.tasks import assign_experiment_credit, update_assignments, check_blacklist, \ - experiment_reward -from expdj.apps.experiments.utils import get_experiment_selection, install_experiments, \ - update_credits, make_results_df, get_battery_results, get_experiment_type, remove_keys, \ - complete_survey_result, select_experiments -from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT,DOMAIN_NAME -from django.http.response import HttpResponseRedirect, HttpResponseForbidden, Http404 -from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect -from django.core.exceptions import PermissionDenied, ValidationError +import datetime +import csv +import hashlib +import json +import numpy +import os +import pandas +import re +import shutil +import uuid + from expfactory.battery import get_load_static, get_experiment_run from expfactory.survey import generate_survey +from expfactory.experiment import load_experiment + from django.contrib.auth.decorators import login_required -from expdj.apps.turk.models import HIT, Result, Assignment -from expdj.apps.turk.models import get_worker, Blacklist, Bonus +from django.core.exceptions import PermissionDenied, ValidationError +from django.forms.models import model_to_dict from django.http import HttpResponse, JsonResponse -from expfactory.experiment import load_experiment +from django.http.response import ( + HttpResponseRedirect, HttpResponseForbidden, Http404 +) +from django.shortcuts import ( + get_object_or_404, render_to_response, render, redirect +) +from django.utils import timezone +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect + from expdj.apps.main.views import google_auth_view -from django.forms.models import model_to_dict -from expfactory.views import embed_experiment -from expdj.apps.users.models import User -from django.shortcuts import render +from expdj.apps.experiments.forms import ( + ExperimentForm, ExperimentTemplateForm, BatteryForm, BlacklistForm +) +from expdj.apps.experiments.models import ( + ExperimentTemplate, Experiment, Battery, ExperimentVariable, + CreditCondition +) +from expdj.apps.experiments.utils import ( + get_experiment_selection, install_experiments, update_credits, + make_results_df, get_battery_results, get_experiment_type, remove_keys, + complete_survey_result, select_experiments +) +from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT,DOMAIN_NAME import expdj.settings as settings -from django.utils import timezone -import datetime -import uuid -import shutil -import hashlib -import numpy -import pandas -import uuid -import json -import csv -import re -import os +from expdj.apps.turk.models import ( + HIT, Result, Assignment, get_worker, Blacklist, Bonus +) +from expdj.apps.turk.tasks import ( + assign_experiment_credit, update_assignments, check_blacklist, + experiment_reward +) +from expdj.apps.turk.utils import get_worker_experiments +from expdj.apps.users.models import User + media_dir = os.path.join(BASE_DIR,MEDIA_ROOT) @@ -398,18 +411,18 @@ def serve_battery(request,bid,userid=None): worker_completed_batteries.append(result.battery) missing_batteries = [] - for required_battery in battery.required_battery_set.all(): + for required_battery in battery.required_batteries.all(): if required_battery not in worker_completed_batteries: missing_batteries.append(required_battery) blocking_batteries = [] - for restricted_battery in battery.restricted_battery_set.all(): + for restricted_battery in battery.restricted_batteries.all(): if restricted_battery in worker_completed_batteries: blocking_battery.append(required_battery) if missing_batteries or blocking_batteries: return render_to_response( - "turk/battery_requirements_not_met.html", + "experiments/battery_requirements_not_met.html", context={'missing_batteries': missing_batteries, 'blocking_batteries': blocking_batteries} ) From e3a2eed466636fff4071eb6f09faa48416002f1e Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Jun 2016 14:28:11 -0700 Subject: [PATCH 006/142] added template to show which battery requirements have not been met --- .../battery_requirements_not_met.html | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html diff --git a/expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html b/expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html new file mode 100644 index 0000000..d2eeb93 --- /dev/null +++ b/expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html @@ -0,0 +1,49 @@ +{% load staticfiles %} + + + The Experiment Factory: Thank you + + + + + + + +
+

Some Requirements Have Not Been Met.

+ {% if missing_batteries %} + The following batteries need to be completed before the current one may be attempted:
+
    + {% for battery in missing_batteries %} +
  • + {{ battery.name }} +
  • + {% endfor %} +
+ {% endif %} + {% if missing_batteries %} + The following batteries conflict with the current battery. Their completion is preventing the current battery from being attempted:
+
    + {% for battery in blocking_batteries %} +
  • + {{ battery.name }} +
  • + {% endfor %} +
+ {% endif %} +
+ + From 3a9cd20e54c1b3f45d4df8e6d41eafd7512a28e4 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Jun 2016 14:32:00 -0700 Subject: [PATCH 007/142] updated template logic to check if correct list is empty in battery requirements not met template --- .../templates/experiments/battery_requirements_not_met.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html b/expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html index d2eeb93..4f5fde2 100644 --- a/expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html +++ b/expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html @@ -34,7 +34,7 @@

Some Requirements Have Not Been Met.

{% endfor %} {% endif %} - {% if missing_batteries %} + {% if blocking_batteries %} The following batteries conflict with the current battery. Their completion is preventing the current battery from being attempted:
    {% for battery in blocking_batteries %} From a7d41cd930da8edfbe275bfcf8121bb5ce7f5927 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Jun 2016 15:22:34 -0700 Subject: [PATCH 008/142] move dependency checking code into the battery model so it can be used by non-local deployments more easily --- expdj/apps/experiments/models.py | 23 +++++++++++++++++++++++ expdj/apps/experiments/views.py | 16 +--------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index 611abd5..b70c871 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -12,6 +12,10 @@ from django.db.models import Q, DO_NOTHING from django.db.models.signals import m2m_changed +# trying to import Result object directly from models was giving an import +# error here, even though the import matched views.py exactly. +from expdj.apps import turk + class CognitiveAtlasConcept(models.Model): name = models.CharField(max_length=1000, null=False, blank=False) cog_atlas_id = models.CharField(primary_key=True, max_length=200, null=False, blank=False) @@ -197,6 +201,25 @@ def save(self, *args, **kwargs): assign_perm('del_battery', self.owner, self) assign_perm('edit_battery', self.owner, self) + def check_battery_dependencies(self, worker_id): + worker_results = turk.models.Result.objects.filter(worker_id=worker_id, + completed=True) + worker_completed_batteries = [] + for result in worker_results: + worker_completed_batteries.append(result.battery) + + missing_batteries = [] + for required_battery in self.required_batteries.all(): + if required_battery not in worker_completed_batteries: + missing_batteries.append(required_battery) + + blocking_batteries = [] + for restricted_battery in self.restricted_batteries.all(): + if restricted_battery in worker_completed_batteries: + blocking_battery.append(required_battery) + + return missing_batteries, blocking_batteries + class Meta: ordering = ["name"] app_label = 'experiments' diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index 8f25fa1..b9dee6b 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -405,21 +405,7 @@ def serve_battery(request,bid,userid=None): if isinstance(worker,list): # no id means returning [] return render_to_response("turk/invalid_id_sorry.html") - worker_results = Result.objects.filter(worker=worker, completed=True) - worker_completed_batteries = [] - for result in worker_results: - worker_completed_batteries.append(result.battery) - - missing_batteries = [] - for required_battery in battery.required_batteries.all(): - if required_battery not in worker_completed_batteries: - missing_batteries.append(required_battery) - - blocking_batteries = [] - for restricted_battery in battery.restricted_batteries.all(): - if restricted_battery in worker_completed_batteries: - blocking_battery.append(required_battery) - + missing_batteries, blocking_batteries = battery.check_battery_dependencies(userid) if missing_batteries or blocking_batteries: return render_to_response( "experiments/battery_requirements_not_met.html", From 71bc4d9260286f374a46a19062c5039371584ec4 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Jun 2016 16:10:13 -0700 Subject: [PATCH 009/142] check battery dependencies on turk preview hit and serve hit --- expdj/apps/turk/views.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 6d12149..4bd3edb 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -208,6 +208,14 @@ def preview_hit(request,hid): hit = get_hit(hid,request) battery = hit.battery context = get_amazon_variables(request) + missing_batteries, blocking_batteries = battery.check_battery_dependencies(userid) + if missing_batteries or blocking_batteries: + return render_to_response( + "experiments/battery_requirements_not_met.html", + context={'missing_batteries': missing_batteries, + 'blocking_batteries': blocking_batteries} + ) + context["instruction_forms"] = get_battery_intro(battery) context["hit_uid"] = hid context["start_url"] = "/accept/%s/?assignmentId=%s&workerId=%s&turkSubmitTo=%s&hitId=%s" %(hid, From 7b622ff38b02256a354d3d3151db72f02fc02325 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Jun 2016 16:10:37 -0700 Subject: [PATCH 010/142] check battery dependencies on turk preview hit and serve hit --- expdj/apps/turk/views.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 4bd3edb..ed5c67f 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -127,6 +127,13 @@ def serve_hit(request,hid): # Get Experiment Factory objects for each worker = get_worker(aws["worker_id"]) + missing_batteries, blocking_batteries = battery.check_battery_dependencies(worker_id) + if missing_batteries or blocking_batteries: + return render_to_response( + "experiments/battery_requirements_not_met.html", + context={'missing_batteries': missing_batteries, + 'blocking_batteries': blocking_batteries} + ) # This is the submit URL, either external or sandbox host = get_host(hit) @@ -208,7 +215,8 @@ def preview_hit(request,hid): hit = get_hit(hid,request) battery = hit.battery context = get_amazon_variables(request) - missing_batteries, blocking_batteries = battery.check_battery_dependencies(userid) + + missing_batteries, blocking_batteries = battery.check_battery_dependencies(context["worker_id"]) if missing_batteries or blocking_batteries: return render_to_response( "experiments/battery_requirements_not_met.html", From c2e7b323e6f9def62b4b2979c67f955f30f7227a Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Jun 2016 16:13:43 -0700 Subject: [PATCH 011/142] fixed how worker_is was being accessed in serve_hit view --- expdj/apps/turk/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index ed5c67f..d281d0c 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -127,7 +127,7 @@ def serve_hit(request,hid): # Get Experiment Factory objects for each worker = get_worker(aws["worker_id"]) - missing_batteries, blocking_batteries = battery.check_battery_dependencies(worker_id) + missing_batteries, blocking_batteries = battery.check_battery_dependencies(aws["worker_id"]) if missing_batteries or blocking_batteries: return render_to_response( "experiments/battery_requirements_not_met.html", From 9de05c3450d7f4e13b6fbc11092f7362a83930b0 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Jun 2016 15:22:29 -0700 Subject: [PATCH 012/142] intermediate work on updating the battery dependency checks to ensure that all experiments in a battery have been completed --- expdj/apps/experiments/models.py | 33 ++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index b70c871..820851e 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -6,6 +6,7 @@ from polymorphic.models import PolymorphicModel from django.contrib.auth.models import User +from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models @@ -202,11 +203,35 @@ def save(self, *args, **kwargs): assign_perm('edit_battery', self.owner, self) def check_battery_dependencies(self, worker_id): - worker_results = turk.models.Result.objects.filter(worker_id=worker_id, - completed=True) - worker_completed_batteries = [] + worker_results = turk.models.Result.objects.filter( + worker_id = worker_id, + completed=True + ) + + worker_result_assignments = {} for result in worker_results: - worker_completed_batteries.append(result.battery) + if worker_result_assignments.get(result.assignment_id) + worker_result_assignments[result.assignment_id].append(result) + else: + worker_result_assignments[result.assignment_id] = [] + worker_result_assignments[result.assignment_id].append(result) + + worker_completed_batteries = [] + for result in worker_result_assignments: + all_experiments_complete = True + result_expirment_list = [x.experiment_id for x in result] + try: + battery_experiments = Battery.objects.get(id=result.battery_id).experiments + except ObjectDoesNotExist: + # battery may have been removed. + continue + for experiment in battery_experiments: + if experiment.id not in result_experiment_list: + all_experiments_complete = False + break + if all_experiments_complete: + worker_completed_batteries.append(battery) + continue missing_batteries = [] for required_battery in self.required_batteries.all(): From 14d5c4169857c87a3eab82aa87384a504a4b7c0b Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Jun 2016 15:45:21 -0700 Subject: [PATCH 013/142] added comments on the how check_batttery_dependencies works --- expdj/apps/experiments/models.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index 820851e..6e6a89b 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -203,6 +203,16 @@ def save(self, *args, **kwargs): assign_perm('edit_battery', self.owner, self) def check_battery_dependencies(self, worker_id): + ''' + check_battery_dependencies looks up all of a workers completed + experiments in a result object and places them in a dictionary + organized by assignment_id. Each of these buckets of results is + iterated through to check that every experiment in a battery has + been completed. In this way a list of batteries that a worker has + completed is built. This list is then compared to the lists of + required and restriected batteries to determine if the worker is + elidgble to attempt the current(self) battery. + ''' worker_results = turk.models.Result.objects.filter( worker_id = worker_id, completed=True From e968408a2eb8532c48ddd258c685f46503831f06 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Jun 2016 15:47:02 -0700 Subject: [PATCH 014/142] added missing colon to check_battery_dependencies --- expdj/apps/experiments/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index 6e6a89b..ee1fc26 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -220,7 +220,7 @@ def check_battery_dependencies(self, worker_id): worker_result_assignments = {} for result in worker_results: - if worker_result_assignments.get(result.assignment_id) + if worker_result_assignments.get(result.assignment_id): worker_result_assignments[result.assignment_id].append(result) else: worker_result_assignments[result.assignment_id] = [] From 745c1b4fcae8147375d6e7bccfaebfd1fe889a49 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Jun 2016 16:30:55 -0700 Subject: [PATCH 015/142] fixed logic and spelling issues in check_battery_dependencies --- expdj/apps/experiments/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index ee1fc26..575da63 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -227,11 +227,12 @@ def check_battery_dependencies(self, worker_id): worker_result_assignments[result.assignment_id].append(result) worker_completed_batteries = [] - for result in worker_result_assignments: + for assignment_id in worker_result_assignments: + result = worker_result_assignments[assignment_id] all_experiments_complete = True - result_expirment_list = [x.experiment_id for x in result] + result_experiment_list = [x.experiment_id for x in result] try: - battery_experiments = Battery.objects.get(id=result.battery_id).experiments + battery_experiments = Battery.objects.get(id=result[0].battery_id).experiments.all() except ObjectDoesNotExist: # battery may have been removed. continue From 83cb89d7a2cd1efe92693a6a49f1d1dbb29336ab Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Jun 2016 16:44:11 -0700 Subject: [PATCH 016/142] experiments_experiment id is not the same as turk_result.experiment_id, updated to use experiments_experiment.template_id --- expdj/apps/experiments/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index 575da63..dacfb56 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -232,12 +232,13 @@ def check_battery_dependencies(self, worker_id): all_experiments_complete = True result_experiment_list = [x.experiment_id for x in result] try: + battery = result[0].battery_id battery_experiments = Battery.objects.get(id=result[0].battery_id).experiments.all() except ObjectDoesNotExist: # battery may have been removed. continue for experiment in battery_experiments: - if experiment.id not in result_experiment_list: + if experiment.template_id not in result_experiment_list: all_experiments_complete = False break if all_experiments_complete: @@ -246,12 +247,12 @@ def check_battery_dependencies(self, worker_id): missing_batteries = [] for required_battery in self.required_batteries.all(): - if required_battery not in worker_completed_batteries: + if required_battery.id not in worker_completed_batteries: missing_batteries.append(required_battery) blocking_batteries = [] for restricted_battery in self.restricted_batteries.all(): - if restricted_battery in worker_completed_batteries: + if restricted_battery.id in worker_completed_batteries: blocking_battery.append(required_battery) return missing_batteries, blocking_batteries From e057da4653012ddd91887b4292ab8ff87c3569a6 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Jun 2016 17:31:19 -0700 Subject: [PATCH 017/142] spelling in check_battery_dependencies comment --- expdj/apps/experiments/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index dacfb56..ca71b71 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -210,8 +210,8 @@ def check_battery_dependencies(self, worker_id): iterated through to check that every experiment in a battery has been completed. In this way a list of batteries that a worker has completed is built. This list is then compared to the lists of - required and restriected batteries to determine if the worker is - elidgble to attempt the current(self) battery. + required and restricted batteries to determine if the worker is + eligible to attempt the current(self) battery. ''' worker_results = turk.models.Result.objects.filter( worker_id = worker_id, From 8ffabfa917e4036fd851b9b6b12b1f68b6eff981 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Tue, 14 Jun 2016 10:44:30 -0700 Subject: [PATCH 018/142] moved check_battery_dependency from battery model to turk/tasks.py. Added check_battery_view to turk views to not repeat myself --- expdj/apps/experiments/models.py | 55 ----------------------- expdj/apps/turk/tasks.py | 76 +++++++++++++++++++++++++++++--- expdj/apps/turk/views.py | 73 ++++++++++++++++-------------- 3 files changed, 111 insertions(+), 93 deletions(-) diff --git a/expdj/apps/experiments/models.py b/expdj/apps/experiments/models.py index dacfb56..83a951f 100644 --- a/expdj/apps/experiments/models.py +++ b/expdj/apps/experiments/models.py @@ -202,61 +202,6 @@ def save(self, *args, **kwargs): assign_perm('del_battery', self.owner, self) assign_perm('edit_battery', self.owner, self) - def check_battery_dependencies(self, worker_id): - ''' - check_battery_dependencies looks up all of a workers completed - experiments in a result object and places them in a dictionary - organized by assignment_id. Each of these buckets of results is - iterated through to check that every experiment in a battery has - been completed. In this way a list of batteries that a worker has - completed is built. This list is then compared to the lists of - required and restriected batteries to determine if the worker is - elidgble to attempt the current(self) battery. - ''' - worker_results = turk.models.Result.objects.filter( - worker_id = worker_id, - completed=True - ) - - worker_result_assignments = {} - for result in worker_results: - if worker_result_assignments.get(result.assignment_id): - worker_result_assignments[result.assignment_id].append(result) - else: - worker_result_assignments[result.assignment_id] = [] - worker_result_assignments[result.assignment_id].append(result) - - worker_completed_batteries = [] - for assignment_id in worker_result_assignments: - result = worker_result_assignments[assignment_id] - all_experiments_complete = True - result_experiment_list = [x.experiment_id for x in result] - try: - battery = result[0].battery_id - battery_experiments = Battery.objects.get(id=result[0].battery_id).experiments.all() - except ObjectDoesNotExist: - # battery may have been removed. - continue - for experiment in battery_experiments: - if experiment.template_id not in result_experiment_list: - all_experiments_complete = False - break - if all_experiments_complete: - worker_completed_batteries.append(battery) - continue - - missing_batteries = [] - for required_battery in self.required_batteries.all(): - if required_battery.id not in worker_completed_batteries: - missing_batteries.append(required_battery) - - blocking_batteries = [] - for restricted_battery in self.restricted_batteries.all(): - if restricted_battery.id in worker_completed_batteries: - blocking_battery.append(required_battery) - - return missing_batteries, blocking_batteries - class Meta: ordering = ["name"] app_label = 'experiments' diff --git a/expdj/apps/turk/tasks.py b/expdj/apps/turk/tasks.py index 7a1d5e4..0a8a741 100644 --- a/expdj/apps/turk/tasks.py +++ b/expdj/apps/turk/tasks.py @@ -1,14 +1,22 @@ from __future__ import absolute_import -from expdj.apps.turk.models import Result, Assignment, get_worker, HIT, Blacklist, Bonus -from expdj.apps.experiments.utils import get_experiment_type -from expdj.apps.experiments.models import ExperimentTemplate, Battery + +import numpy +import os + from boto.mturk.price import Price from celery import shared_task, Celery -from django.utils import timezone + from django.conf import settings +from django.utils import timezone + +from expdj.apps.experiments.models import ExperimentTemplate, Battery +from expdj.apps.experiments.utils import get_experiment_type +from expdj.apps.turk.models import Result, Assignment, get_worker, HIT, Blacklist, Bonus from expdj.settings import TURK -import numpy -import os + +# trying to import Result object directly from models was giving an import +# error here, even though the import matched views.py exactly. +from expdj.apps import turk os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'expdj.settings') app = Celery('expdj') @@ -283,3 +291,59 @@ def get_unique_variables(results): new_variables = [x for x in trial["trialdata"].keys() if x not in variables] variables = variables + new_variables return numpy.unique(variables).tolist() + + +def check_battery_dependencies(current_battery, worker_id): + ''' + check_battery_dependencies looks up all of a workers completed + experiments in a result object and places them in a dictionary + organized by assignment_id. Each of these buckets of results is + iterated through to check that every experiment in a battery has + been completed. In this way a list of batteries that a worker has + completed is built. This list is then compared to the lists of + required and restriected batteries to determine if the worker is + elidgble to attempt the current battery. + ''' + worker_results = turk.models.Result.objects.filter( + worker_id = worker_id, + completed=True + ) + + worker_result_assignments = {} + for result in worker_results: + if worker_result_assignments.get(result.assignment_id): + worker_result_assignments[result.assignment_id].append(result) + else: + worker_result_assignments[result.assignment_id] = [] + worker_result_assignments[result.assignment_id].append(result) + + worker_completed_batteries = [] + for assignment_id in worker_result_assignments: + result = worker_result_assignments[assignment_id] + all_experiments_complete = True + result_experiment_list = [x.experiment_id for x in result] + try: + battery_id = result[0].battery_id + battery_experiments = Battery.objects.get(id=battery_id).experiments.all() + except ObjectDoesNotExist: + # battery may have been removed. + continue + for experiment in battery_experiments: + if experiment.template_id not in result_experiment_list: + all_experiments_complete = False + break + if all_experiments_complete: + worker_completed_batteries.append(battery_id) + continue + + missing_batteries = [] + for required_battery in current_battery.required_batteries.all(): + if required_battery.id not in worker_completed_batteries: + missing_batteries.append(required_battery) + + blocking_batteries = [] + for restricted_battery in current_battery.restricted_batteries.all(): + if restricted_battery.id in worker_completed_batteries: + blocking_batteries.append(restricted_battery) + + return missing_batteries, blocking_batteries diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index d281d0c..0d13cde 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -1,25 +1,30 @@ -from expdj.apps.experiments.views import check_battery_edit_permission, check_mturk_access, \ -get_battery_intro, deploy_battery -from expdj.apps.turk.utils import get_connection, get_worker_url, get_host, get_worker_experiments -from django.http.response import HttpResponseRedirect, HttpResponseForbidden, HttpResponse, Http404 -from django.shortcuts import get_object_or_404, render_to_response, render, redirect -from expdj.apps.turk.tasks import assign_experiment_credit, get_unique_experiments -from expdj.apps.experiments.utils import get_experiment_type, select_experiments -from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker -from expdj.apps.experiments.models import Battery, ExperimentTemplate +from datetime import timedelta, datetime +import json +import os +import requests + from expfactory.battery import get_load_static, get_experiment_run -from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT +from numpy.random import choice +from optparse import make_option + from django.contrib.auth.decorators import login_required from django.core.management.base import BaseCommand +from django.http.response import HttpResponseRedirect, HttpResponseForbidden, HttpResponse, Http404 +from django.shortcuts import get_object_or_404, render_to_response, render, redirect +from django.utils import timezone from django.views.decorators.csrf import ensure_csrf_cookie + +from expdj.apps.experiments.models import Battery, ExperimentTemplate +from expdj.apps.experiments.views import check_battery_edit_permission, check_mturk_access, \ +get_battery_intro, deploy_battery +from expdj.apps.experiments.utils import get_experiment_type, select_experiments from expdj.apps.turk.forms import HITForm -from datetime import timedelta, datetime -from django.utils import timezone -from optparse import make_option -from numpy.random import choice -import requests -import json -import os +from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker +from expdj.apps.turk.tasks import (assign_experiment_credit, + check_battery_dependencies, get_unique_experiments) +from expdj.apps.turk.utils import (get_connection, get_host, get_worker_url, + get_worker_experiments) +from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT media_dir = os.path.join(BASE_DIR,MEDIA_ROOT) @@ -127,13 +132,10 @@ def serve_hit(request,hid): # Get Experiment Factory objects for each worker = get_worker(aws["worker_id"]) - missing_batteries, blocking_batteries = battery.check_battery_dependencies(aws["worker_id"]) - if missing_batteries or blocking_batteries: - return render_to_response( - "experiments/battery_requirements_not_met.html", - context={'missing_batteries': missing_batteries, - 'blocking_batteries': blocking_batteries} - ) + check_battery_response = check_battery_view(battery, aws["worker_id"]) + if (check_battery_response): + return check_battery_response + # This is the submit URL, either external or sandbox host = get_host(hit) @@ -216,14 +218,10 @@ def preview_hit(request,hid): battery = hit.battery context = get_amazon_variables(request) - missing_batteries, blocking_batteries = battery.check_battery_dependencies(context["worker_id"]) - if missing_batteries or blocking_batteries: - return render_to_response( - "experiments/battery_requirements_not_met.html", - context={'missing_batteries': missing_batteries, - 'blocking_batteries': blocking_batteries} - ) - + check_battery_response = check_battery_view(battery, context["worker_id"]) + if (check_battery_response): + return check_battery_response + context["instruction_forms"] = get_battery_intro(battery) context["hit_uid"] = hid context["start_url"] = "/accept/%s/?assignmentId=%s&workerId=%s&turkSubmitTo=%s&hitId=%s" %(hid, @@ -394,3 +392,14 @@ def get_flagged_questions(number=None): if number == None: return questions return choice(questions,int(number)) + +def check_battery_view(battery, worker_id): + missing_batteries, blocking_batteries = check_battery_dependencies(battery, worker_id) + if missing_batteries or blocking_batteries: + return render_to_response( + "experiments/battery_requirements_not_met.html", + context={'missing_batteries': missing_batteries, + 'blocking_batteries': blocking_batteries} + ) + else: + return None From 4703419a0267675f218146ec5d52a2b147b35054 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Tue, 14 Jun 2016 13:00:41 -0700 Subject: [PATCH 019/142] added url, view, and serializers for a listing of batteries with ownership checks --- expdj/apps/experiments/api_views.py | 13 ++++++++++ expdj/apps/experiments/serializers.py | 25 ++++++++++++++++++ expdj/apps/experiments/urls.py | 37 +++++++++++++++++---------- 3 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 expdj/apps/experiments/api_views.py create mode 100644 expdj/apps/experiments/serializers.py diff --git a/expdj/apps/experiments/api_views.py b/expdj/apps/experiments/api_views.py new file mode 100644 index 0000000..ddb8faf --- /dev/null +++ b/expdj/apps/experiments/api_views.py @@ -0,0 +1,13 @@ +from rest_framework import generics +from rest_framework import permissions +from rest_framework import viewsets + +from expdj.apps.experiments.models import Battery +from expdj.apps.experiments.serializers import BatterySerializer + +class BatteryAPIList(generics.ListAPIView): + serializer_class = BatterySerializer + + def get_queryset(self): + queryset = Battery.objects.filter(owner=self.request.user) + return queryset diff --git a/expdj/apps/experiments/serializers.py b/expdj/apps/experiments/serializers.py new file mode 100644 index 0000000..5320466 --- /dev/null +++ b/expdj/apps/experiments/serializers.py @@ -0,0 +1,25 @@ +from rest_framework import serializers + +from expdj.apps.experiments.models import Battery + +class BatterySerializer(serializers.ModelSerializer): + owner = serializers.StringRelatedField() + contributors = serializers.StringRelatedField(many=True) + experiments = serializers.StringRelatedField(many=True) + required_batteries = serializers.StringRelatedField(many=True) + restricted_batteries = serializers.StringRelatedField(many=True) + + class Meta: + model = Battery + fields = [ + 'name', 'description', 'credentials', 'consent', 'advertisement', + 'instructions', 'owner', 'contributors', 'experiments', 'add_date', + 'modify_date', 'maximum_time', 'number_of_experiments', 'active', + 'presentation_order', 'blacklist_active', 'blacklist_threshold', + 'bonus_active', 'required_batteries', 'restricted_batteries' + ] + +class BatteryDescriptionSerializer(serializers.ModelSerializer): + class Meta: + model = Battery + fields = ['name', 'description'] diff --git a/expdj/apps/experiments/urls.py b/expdj/apps/experiments/urls.py index 44d58c5..f2b7529 100644 --- a/expdj/apps/experiments/urls.py +++ b/expdj/apps/experiments/urls.py @@ -1,16 +1,21 @@ -from expdj.apps.experiments.views import experiments_view, edit_experiment_template, \ -delete_experiment_template, add_experiment_template, save_experiment_template, \ -view_experiment, preview_experiment, batteries_view, add_battery, \ -edit_battery, view_battery, delete_battery, remove_experiment, \ -add_experiment, edit_experiment, save_experiment, update_experiment_template, \ -remove_condition, preview_battery, serve_battery, serve_battery_anon, \ -generate_battery_user, sync, experiment_results_dashboard, \ -battery_results_dashboard, dummy_battery ,modify_experiment, intro_battery, \ -save_survey_template, add_survey_template, add_game_template, save_game_template, \ -enable_cookie_view, change_experiment_order, serve_battery_gmail, subject_management -from expdj import settings -from django.views.generic.base import TemplateView from django.conf.urls import patterns, url +from django.views.generic.base import TemplateView + +from expdj import settings +from expdj.apps.experiments.api_views import BatteryAPIList +from expdj.apps.experiments.views import ( + experiments_view, edit_experiment_template, delete_experiment_template, + add_experiment_template, save_experiment_template, view_experiment, + preview_experiment, batteries_view, add_battery, edit_battery, + view_battery, delete_battery, remove_experiment, add_experiment, + edit_experiment, save_experiment, update_experiment_template, + remove_condition, preview_battery, serve_battery, serve_battery_anon, + generate_battery_user, sync, experiment_results_dashboard, + battery_results_dashboard, dummy_battery ,modify_experiment, intro_battery, + save_survey_template, add_survey_template, add_game_template, + save_game_template, enable_cookie_view, change_experiment_order, + serve_battery_gmail, subject_management +) urlpatterns = patterns('', @@ -66,11 +71,15 @@ url(r'^batteries/(?P\d+|[A-Z]{8})/serve/gmail$',serve_battery_gmail,name='serve_battery_gmail'), url(r'^local/(?P\d+|[A-Z]{8})/$',sync,name='local'), url(r'^local/$',sync,name='local'), # local sync of data - url(r'^cookie/$',enable_cookie_view,name='enable_cookie_view')) + url(r'^cookie/$',enable_cookie_view,name='enable_cookie_view'), + + # API + url(r'^api_/battery/$', BatteryAPIList.as_view(), name='battery_api_list'), +) if settings.DEBUG: urlpatterns += patterns('', url(r'^static/(?P.*)$', 'django.views.static.serve', { 'document_root': settings.MEDIA_ROOT, }), - ) +) From 6975073c2e16d2eb80b09af3ba058469ac106f89 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Tue, 14 Jun 2016 13:34:43 -0700 Subject: [PATCH 020/142] copied over old serializers from base url conf to new experiments and turk serializers files, added result list views to view all results owned by a user and all results for a given battery that are owned by a user --- expdj/apps/experiments/serializers.py | 18 +++++++++++++++++- expdj/apps/turk/api_views.py | 19 +++++++++++++++++++ expdj/apps/turk/serializers.py | 24 ++++++++++++++++++++++++ expdj/apps/turk/urls.py | 25 +++++++++++++++++++------ 4 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 expdj/apps/turk/api_views.py create mode 100644 expdj/apps/turk/serializers.py diff --git a/expdj/apps/experiments/serializers.py b/expdj/apps/experiments/serializers.py index 5320466..30a138b 100644 --- a/expdj/apps/experiments/serializers.py +++ b/expdj/apps/experiments/serializers.py @@ -1,6 +1,8 @@ from rest_framework import serializers -from expdj.apps.experiments.models import Battery +from expdj.apps.experiments.models import ( + Battery, CognitiveAtlasTask, ExperimentTemplate +) class BatterySerializer(serializers.ModelSerializer): owner = serializers.StringRelatedField() @@ -23,3 +25,17 @@ class BatteryDescriptionSerializer(serializers.ModelSerializer): class Meta: model = Battery fields = ['name', 'description'] + +class CognitiveAtlasTaskSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = CognitiveAtlasTask + fields = ('name','cog_atlas_id') + +class ExperimentTemplateSerializer(serializers.HyperlinkedModelSerializer): + cognitive_atlas_task = CognitiveAtlasTaskSerializer() + class Meta: + model = ExperimentTemplate + fields = [ + 'exp_id','name','cognitive_atlas_task','reference','version', + 'template' + ] diff --git a/expdj/apps/turk/api_views.py b/expdj/apps/turk/api_views.py new file mode 100644 index 0000000..9579d29 --- /dev/null +++ b/expdj/apps/turk/api_views.py @@ -0,0 +1,19 @@ +from rest_framework import generics +from rest_framework import permissions +from rest_framework import viewsets + +from expdj.apps.turk.models import Result +from expdj.apps.turk.serializers import ResultSerializer + +class ResultAPIList(generics.ListAPIView): + serializer_class = ResultSerializer + + def get_queryset(self): + queryset = Result.objects.filter(battery__owner=self.request.user) + return queryset + +class BatteryResultAPIList(ResultAPIList): + def get_queryset(self): + queryset = super(BatteryResultAPIList, self).get_queryset() + battery_id = self.kwargs.get('bid') + return queryset.filter(battery__id=battery_id) diff --git a/expdj/apps/turk/serializers.py b/expdj/apps/turk/serializers.py new file mode 100644 index 0000000..a8f411e --- /dev/null +++ b/expdj/apps/turk/serializers.py @@ -0,0 +1,24 @@ +from rest_framework import serializers + +from expdj.apps.experiments.serializers import ( + BatteryDescriptionSerializer, ExperimentTemplateSerializer +) +from expdj.apps.turk.models import Result +from expdj.apps.turk.utils import to_dict + +# class ResultSerializer(serializers.ModelSerializer): +class ResultSerializer(serializers.HyperlinkedModelSerializer): + experiment = ExperimentTemplateSerializer() + battery = BatteryDescriptionSerializer() + worker = serializers.StringRelatedField() + data = serializers.SerializerMethodField('get_taskdata') + + def get_taskdata(self, result): + return to_dict(result.taskdata) + + class Meta: + model = Result + fields = [ + 'data','experiment','battery','worker','language','browser', + 'platform','completed','finishtime' + ] diff --git a/expdj/apps/turk/urls.py b/expdj/apps/turk/urls.py index 1bdf124..528ac3c 100644 --- a/expdj/apps/turk/urls.py +++ b/expdj/apps/turk/urls.py @@ -1,9 +1,13 @@ -from expdj.apps.turk.views import edit_hit, delete_hit, expire_hit, preview_hit, \ -serve_hit, multiple_new_hit, end_assignment, finished_view, not_consent_view, \ -survey_submit, manage_hit -from expdj.apps.experiments.views import sync -from django.views.generic.base import TemplateView from django.conf.urls import patterns, url +from django.views.generic.base import TemplateView + +from expdj.apps.experiments.views import sync +from expdj.apps.turk.api_views import ResultAPIList, BatteryResultAPIList +from expdj.apps.turk.views import ( + edit_hit, delete_hit, expire_hit, preview_hit, serve_hit, multiple_new_hit, + end_assignment, finished_view, not_consent_view, survey_submit, manage_hit +) + urlpatterns = patterns('', # HITS @@ -22,5 +26,14 @@ url(r'^surveys/(?P\d+|[A-Z]{8})/(?P[A-Za-z0-9]{30})/submit$',survey_submit,name='survey_submit'), url(r'^sync/(?P\d+|[A-Z]{8})/$',sync,name='sync_data'), url(r'^sync/$',sync,name='sync_data'), - url(r'^finished$', finished_view, name="finished_view") + url(r'^finished$', finished_view, name="finished_view"), + + # API + url(r'^api_/results/$', ResultAPIList.as_view(), name='result_api_list'), + url( + r'^api_/results/(?P\d+)/$', + BatteryResultAPIList.as_view(), + name='battery_result_api_list' + ) + ) From cbbe504e532da8e9fbd5fbe21066f1c88f0bb07e Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Jun 2016 13:21:23 -0700 Subject: [PATCH 021/142] add creation time to hit list in battery detail template --- .../apps/experiments/templates/experiments/battery_details.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/expdj/apps/experiments/templates/experiments/battery_details.html b/expdj/apps/experiments/templates/experiments/battery_details.html index e790c07..a0dd524 100644 --- a/expdj/apps/experiments/templates/experiments/battery_details.html +++ b/expdj/apps/experiments/templates/experiments/battery_details.html @@ -157,6 +157,7 @@

    Amazon Mechanical Turk HITS

    + @@ -170,6 +171,7 @@

    Amazon Mechanical Turk HITS

    {% else %} {% endif %} + From 59362082564bc60ba62e32491617c7d383b2f09c Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Jun 2016 13:45:55 -0700 Subject: [PATCH 022/142] set default ordering to creation time column in hits list in battery detail template --- .../experiments/templates/experiments/battery_details.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expdj/apps/experiments/templates/experiments/battery_details.html b/expdj/apps/experiments/templates/experiments/battery_details.html index a0dd524..812d9be 100644 --- a/expdj/apps/experiments/templates/experiments/battery_details.html +++ b/expdj/apps/experiments/templates/experiments/battery_details.html @@ -263,7 +263,8 @@ ] }); $('#hits_table').dataTable({ - responsive: true + responsive: true, + "order": [[ 1, "desc" ]] }); $('#delete_experiment').click(function(e) { return confirm("This will remove the experiment from the battery, and not delete it from the application. Are you sure you want to do this?"); From 8bc84e9b062abfe91cd361f017cb2becc4f612bb Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Jun 2016 14:35:42 -0700 Subject: [PATCH 023/142] added clone hit button to hit list in battery detail. added url and view to handle cloning a hit in the turk app --- .../experiments/battery_details.html | 1 + expdj/apps/turk/urls.py | 4 +- expdj/apps/turk/views.py | 60 +++++++++++++------ 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/expdj/apps/experiments/templates/experiments/battery_details.html b/expdj/apps/experiments/templates/experiments/battery_details.html index 812d9be..0162603 100644 --- a/expdj/apps/experiments/templates/experiments/battery_details.html +++ b/expdj/apps/experiments/templates/experiments/battery_details.html @@ -178,6 +178,7 @@

    Amazon Mechanical Turk HITS

    + @@ -167,6 +168,7 @@

    Approved

    + {% endfor %} From d551fe6f752921c889683be744f6910f4b445eac Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 30 Jun 2016 15:10:04 -0700 Subject: [PATCH 032/142] added template for contact worker form --- .../templates/turk/contact_worker_modal.html | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 expdj/apps/turk/templates/turk/contact_worker_modal.html diff --git a/expdj/apps/turk/templates/turk/contact_worker_modal.html b/expdj/apps/turk/templates/turk/contact_worker_modal.html new file mode 100644 index 0000000..8fdb15e --- /dev/null +++ b/expdj/apps/turk/templates/turk/contact_worker_modal.html @@ -0,0 +1,19 @@ +{% load crispy_forms_tags %} +
    titlecreation time keywords description reward
    {{ hit.title }}{{ hit.creation_time|date:"m/d/y G:H" }} {{ hit.keywords }} {{ hit.description }} {{ hit.reward }} {% if edit_permission %} Manage Hit + Clone Hit Expire Hit {% if hit.status = "D" %} diff --git a/expdj/apps/turk/urls.py b/expdj/apps/turk/urls.py index 528ac3c..0d2819d 100644 --- a/expdj/apps/turk/urls.py +++ b/expdj/apps/turk/urls.py @@ -5,7 +5,8 @@ from expdj.apps.turk.api_views import ResultAPIList, BatteryResultAPIList from expdj.apps.turk.views import ( edit_hit, delete_hit, expire_hit, preview_hit, serve_hit, multiple_new_hit, - end_assignment, finished_view, not_consent_view, survey_submit, manage_hit + end_assignment, finished_view, not_consent_view, survey_submit, manage_hit, + clone_hit ) @@ -13,6 +14,7 @@ # HITS url(r'^hits/(?P\d+|[A-Z]{8})/new$',edit_hit,name='new_hit'), url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/manage$',manage_hit,name='manage_hit'), + url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/clone$',clone_hit,name='clone_hit'), url(r'^hits/(?P\d+|[A-Z]{8})/multiple$',multiple_new_hit,name='multiple_new_hit'), url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/edit$',edit_hit,name='edit_hit'), url(r'^hits/(?P\d+|[A-Z]{8})/delete$',delete_hit,name='delete_hit'), diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index d281d0c..0646e4c 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -1,25 +1,31 @@ -from expdj.apps.experiments.views import check_battery_edit_permission, check_mturk_access, \ -get_battery_intro, deploy_battery -from expdj.apps.turk.utils import get_connection, get_worker_url, get_host, get_worker_experiments -from django.http.response import HttpResponseRedirect, HttpResponseForbidden, HttpResponse, Http404 -from django.shortcuts import get_object_or_404, render_to_response, render, redirect -from expdj.apps.turk.tasks import assign_experiment_credit, get_unique_experiments -from expdj.apps.experiments.utils import get_experiment_type, select_experiments -from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker -from expdj.apps.experiments.models import Battery, ExperimentTemplate +from datetime import timedelta, datetime +import json +import os +import requests + from expfactory.battery import get_load_static, get_experiment_run -from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT +from numpy.random import choice +from optparse import make_option + from django.contrib.auth.decorators import login_required from django.core.management.base import BaseCommand +from django.core.urlresolvers import reverse +from django.http.response import HttpResponseRedirect, HttpResponseForbidden, HttpResponse, Http404 +from django.shortcuts import get_object_or_404, render_to_response, render, redirect +from django.utils import timezone from django.views.decorators.csrf import ensure_csrf_cookie + +from expdj.apps.experiments.models import Battery, ExperimentTemplate +from expdj.apps.experiments.views import check_battery_edit_permission, check_mturk_access, \ +get_battery_intro, deploy_battery +from expdj.apps.experiments.utils import get_experiment_type, select_experiments from expdj.apps.turk.forms import HITForm -from datetime import timedelta, datetime -from django.utils import timezone -from optparse import make_option -from numpy.random import choice -import requests -import json -import os +from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker +from expdj.apps.turk.tasks import (assign_experiment_credit, + get_unique_experiments) +from expdj.apps.turk.utils import (get_connection, get_host, get_worker_url, + get_worker_experiments) +from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT media_dir = os.path.join(BASE_DIR,MEDIA_ROOT) @@ -305,6 +311,26 @@ def multiple_new_hit(request, bid): else: return HttpResponseForbidden() +@login_required +def clone_hit(request, bid, hid): + mturk_permission = check_mturk_access(request) + if mturk_permission != True: + return HttpResponseForbidden() + + new_hit = get_object_or_404(HIT, pk=hid) + new_hit.pk = None + form = HITForm(instance=new_hit) + form.helper.form_action = reverse('new_hit',args=[bid]) + + battery = Battery.objects.get(pk=bid) + header_text = "%s HIT" %(battery.name) + + context = {"form": form, + "is_owner": True, + "header_text":header_text} + + return render(request, "turk/new_hit.html", context) + @login_required def edit_hit(request, bid, hid=None): From 07f6ea715a10facb37ef89013147cd977d5646fc Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Jun 2016 15:52:47 -0700 Subject: [PATCH 024/142] removed checking for battery completion within assignments only. Now checking all Result objects for a given battery, and if all the experiments for that battery have been completed then the battery is considered completed. --- expdj/apps/turk/tasks.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/expdj/apps/turk/tasks.py b/expdj/apps/turk/tasks.py index e949f88..53743ea 100644 --- a/expdj/apps/turk/tasks.py +++ b/expdj/apps/turk/tasks.py @@ -297,8 +297,8 @@ def check_battery_dependencies(current_battery, worker_id): ''' check_battery_dependencies looks up all of a workers completed experiments in a result object and places them in a dictionary - organized by assignment_id. Each of these buckets of results is - iterated through to check that every experiment in a battery has + organized by battery_id. Each of these buckets of results is + iterated through to check that every experiment in that battery has been completed. In this way a list of batteries that a worker has completed is built. This list is then compared to the lists of required and restricted batteries to determine if the worker is @@ -309,21 +309,20 @@ def check_battery_dependencies(current_battery, worker_id): completed=True ) - worker_result_assignments = {} + worker_result_batteries = {} for result in worker_results: - if worker_result_assignments.get(result.assignment_id): - worker_result_assignments[result.assignment_id].append(result) + if worker_result_batteries.get(result.battery.id): + worker_result_batteries[result.battery.id].append(result) else: - worker_result_assignments[result.assignment_id] = [] - worker_result_assignments[result.assignment_id].append(result) + worker_result_batteries[result.battery.id] = [] + worker_result_batteries[result.battery.id].append(result) worker_completed_batteries = [] - for assignment_id in worker_result_assignments: - result = worker_result_assignments[assignment_id] + for battery_id in worker_result_batteries: + result = worker_result_batteries[battery_id] all_experiments_complete = True result_experiment_list = [x.experiment_id for x in result] try: - battery_id = result[0].battery_id battery_experiments = Battery.objects.get(id=battery_id).experiments.all() except ObjectDoesNotExist: # battery may have been removed. From 63af935d094d55b25ae9b30c1088c358e7749060 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Jun 2016 15:17:52 -0700 Subject: [PATCH 025/142] add number of experiments left message to code that is executed client side between experiments in a battery --- expdj/apps/experiments/views.py | 34 +++++++++++++++++++++------------ expdj/apps/turk/views.py | 28 +++++++++++++++------------ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index ce3f2a0..28a246c 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -399,7 +399,8 @@ def serve_battery(request,bid,userid=None): # Does the worker have experiments remaining? uncompleted_experiments = get_worker_experiments(worker,battery) - if len(uncompleted_experiments) == 0: + experiments_left = len(uncompleted_experiments) + if experiments_left == 0: # Thank you for your participation - no more experiments! return render_to_response("turk/worker_sorry.html") @@ -419,22 +420,27 @@ def serve_battery(request,bid,userid=None): "uniqueId":result.id} # If this is the last experiment, the finish button will link to a thank you page. - if len(uncompleted_experiments) == 1: + if experiments_left == 1: next_page = "/finished" # Determine template name based on template_type template = "%s/serve_battery.html" %(experiment_type) - return deploy_battery(deployment="docker-local", - battery=battery, - experiment_type=experiment_type, - context=context, - task_list=task_list, - template=template, - next_page=next_page, - result=result) - -def deploy_battery(deployment,battery,experiment_type,context,task_list,template,result,next_page=None,last_experiment=False): + return deploy_battery( + deployment="docker-local", + battery=battery, + experiment_type=experiment_type, + context=context, + task_list=task_list, + template=template, + next_page=next_page, + result=result, + experiments_left=experiments_left-1 + ) + +def deploy_battery(deployment, battery, experiment_type, context, task_list, + template, result, next_page=None, last_experiment=False, + experiments_left=None): '''deploy_battery is a general function for returning the final view to deploy a battery, either local or MTurk :param deployment: either "docker-mturk" or "docker-local" :param battery: models.Battery object @@ -471,6 +477,10 @@ def deploy_battery(deployment,battery,experiment_type,context,task_list,template if result != None: runcode = runcode.replace("{{result.id}}",str(result.id)) runcode = runcode.replace("{{next_page}}",next_page) + if experiments_left: + expleft_msg = "

    Experiments left in battery: {0:d}

    " + expleft_msg = expleft_msg.format(experiments_left) + runcode = runcode.replace("

    ", expleft_msg) elif experiment_type in ["games"]: experiment = load_experiment(experiment_folders[0]) runcode = experiment[0]["deployment_variables"]["run"] diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 6d12149..70e060f 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -152,13 +152,14 @@ def serve_hit(request,hid): # Does the worker have experiments remaining for the hit? uncompleted_experiments = get_worker_experiments(worker,hit.battery) - if len(uncompleted_experiments) == 0: + experiments_left = len(uncompleted_experiments) + if experiments_left == 0: # Thank you for your participation - no more experiments! return render_to_response("turk/worker_sorry.html") # if it's the last experiment, we will submit the result to amazon (only for surveys) last_experiment = False - if len(uncompleted_experiments) == 1: + if experiments_left == 1: last_experiment = True task_list = select_experiments(battery,uncompleted_experiments) @@ -180,18 +181,21 @@ def serve_hit(request,hid): aws["uniqueId"] = result.id # If this is the last experiment, the finish button will link to a thank you page. - if len(uncompleted_experiments) == 1: + if experiments_left == 1: next_page = "/finished" - return deploy_battery(deployment="docker-mturk", - battery=battery, - experiment_type=experiment_type, - context=aws, - task_list=task_list, - template=template, - next_page=None, - result=result, - last_experiment=last_experiment) + return deploy_battery( + deployment="docker-mturk", + battery=battery, + experiment_type=experiment_type, + context=aws, + task_list=task_list, + template=template, + next_page=None, + result=result, + last_experiment=last_experiment, + experiments_left=experiments_left-1 + ) else: return render_to_response("turk/error_sorry.html") From 248f0022667af83805c2c31a837541779ae49c15 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Jun 2016 15:28:45 -0700 Subject: [PATCH 026/142] added doc string for new argument to deploy_battery, should fix #156 --- expdj/apps/experiments/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index 28a246c..9d4ee25 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -451,6 +451,7 @@ def deploy_battery(deployment, battery, experiment_type, context, task_list, :param template: html template to render :param result: the result object, turk.models.Result :param last_experiment: boolean if true will redirect the user to a page to submit the result (for surveys) + :param experiments_left: integer indicating how many experiments are left in battery. ''' if next_page == None: next_page = "javascript:window.location.reload();" From 29f6ba202cf660ca80c37579840a5dcbe9e5ad6b Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Tue, 28 Jun 2016 13:04:31 -0700 Subject: [PATCH 027/142] updated local battery server to pull check dependencies from turk utils.py --- expdj/apps/experiments/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index b9dee6b..f16be6e 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -46,7 +46,7 @@ ) from expdj.apps.turk.tasks import ( assign_experiment_credit, update_assignments, check_blacklist, - experiment_reward + experiment_reward, check_battery_dependencies ) from expdj.apps.turk.utils import get_worker_experiments from expdj.apps.users.models import User @@ -405,7 +405,7 @@ def serve_battery(request,bid,userid=None): if isinstance(worker,list): # no id means returning [] return render_to_response("turk/invalid_id_sorry.html") - missing_batteries, blocking_batteries = battery.check_battery_dependencies(userid) + missing_batteries, blocking_batteries = check_battery_dependencies(battery, userid) if missing_batteries or blocking_batteries: return render_to_response( "experiments/battery_requirements_not_met.html", From 452ad67673b39d81091ce31f89230eb0c6399250 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Tue, 28 Jun 2016 14:14:04 -0700 Subject: [PATCH 028/142] for unmet dependency view in mturk need to set x frame options --- expdj/apps/turk/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 0d13cde..b4e9118 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -220,6 +220,7 @@ def preview_hit(request,hid): check_battery_response = check_battery_view(battery, context["worker_id"]) if (check_battery_response): + check_battery_response['x-frame-options'] = 'nobody_ever_goes_in_nobody_ever_goes_out' return check_battery_response context["instruction_forms"] = get_battery_intro(battery) From 63ea452f171449e43d006eeeeb8aa73888998a96 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 29 Jun 2016 16:45:56 -0700 Subject: [PATCH 029/142] Add form to contact workers with --- expdj/apps/turk/forms.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/expdj/apps/turk/forms.py b/expdj/apps/turk/forms.py index eb35dad..d5674a3 100644 --- a/expdj/apps/turk/forms.py +++ b/expdj/apps/turk/forms.py @@ -31,3 +31,12 @@ def __init__(self, *args, **kwargs): self.helper.layout = Layout() tab_holder = TabHolder() self.helper.add_input(Submit("submit", "Save")) + +class WorkerContactForm(forms.Form): + subject = forms.CharField(label="Subject") + message = forms.CharField(label="Message") + + def __init__(self, *args, **kwargs): + super(ContactForm, self).__init__(*args, **kwargs) + self.helper = FormHelper(self) + self.helper.add_input(Submit("submit", "Send")) From dc1a96551dd661c2f2f2c0a1b290b35dbcf65265 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 29 Jun 2016 16:47:04 -0700 Subject: [PATCH 030/142] add contact worker function to assignment model, this model has access to all the information we need to generate connections and send information to workers. Add contact worker view to handle creating and processing the form --- expdj/apps/turk/models.py | 13 +++++++- expdj/apps/turk/views.py | 69 +++++++++++++++++++++++++++++---------- 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/expdj/apps/turk/models.py b/expdj/apps/turk/models.py index 5319836..2d54ce4 100644 --- a/expdj/apps/turk/models.py +++ b/expdj/apps/turk/models.py @@ -407,7 +407,6 @@ class Assignment(models.Model): (True, 'Completed')), default=False,verbose_name="participant completed the entire assignment") - def create(self): init_connection_callback(sender=self.hit) @@ -480,6 +479,18 @@ def update(self, mturk_assignment=None, hit=None): self.save() + def contact_worker(self, subject, msg): + '''contact_worker uses the boto function notify_workers to send a + message a specific worker. + :subject: Subject of the message to send. + :msg: Contents of the message to send. + ''' + self.hit.generate_connection() + try: + self.hit.connection.notify_workers(self.worker.id, subject, msg) + except MTurkRequestError: + pass + def __unicode__(self): return self.mturk_id diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 6d12149..7bd9145 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -1,25 +1,32 @@ -from expdj.apps.experiments.views import check_battery_edit_permission, check_mturk_access, \ -get_battery_intro, deploy_battery -from expdj.apps.turk.utils import get_connection, get_worker_url, get_host, get_worker_experiments -from django.http.response import HttpResponseRedirect, HttpResponseForbidden, HttpResponse, Http404 -from django.shortcuts import get_object_or_404, render_to_response, render, redirect -from expdj.apps.turk.tasks import assign_experiment_credit, get_unique_experiments -from expdj.apps.experiments.utils import get_experiment_type, select_experiments -from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker -from expdj.apps.experiments.models import Battery, ExperimentTemplate +from datetime import timedelta, datetime +import json +import os +import requests + from expfactory.battery import get_load_static, get_experiment_run -from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT +from numpy.random import choice +from optparse import make_option + from django.contrib.auth.decorators import login_required from django.core.management.base import BaseCommand -from django.views.decorators.csrf import ensure_csrf_cookie -from expdj.apps.turk.forms import HITForm -from datetime import timedelta, datetime +from django.http.response import (HttpResponseRedirect, HttpResponseForbidden, + HttpResponse, Http404, HttpResponseNotAllowed) +from django.shortcuts import get_object_or_404, render_to_response, render, redirect from django.utils import timezone -from optparse import make_option -from numpy.random import choice -import requests -import json -import os +from django.views.decorators.csrf import ensure_csrf_cookie + +from expdj.apps.experiments.models import (Assignment, Battery, + ExperimentTemplate) +from expdj.apps.experiments.views import (check_battery_edit_permission, + check_mturk_access, get_battery_intro, deploy_battery) +from expdj.apps.experiments.utils import get_experiment_type, select_experiments +from expdj.apps.turk.forms import HITForm, WorkerContactForm +from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker +from expdj.apps.turk.tasks import (assign_experiment_credit, + check_battery_dependencies, get_unique_experiments) +from expdj.apps.turk.utils import (get_connection, get_host, get_worker_url, + get_worker_experiments) +from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT media_dir = os.path.join(BASE_DIR,MEDIA_ROOT) @@ -329,6 +336,32 @@ def edit_hit(request, bid, hid=None): else: return HttpResponseForbidden() +@login_required +def contact_worker(request, aid): + mturk_permission = check_mturk_access(request) + + if mturk_permission == False: + return HttpResponseForbidden() + + assignment = Assignments.objects.get(aid) + if request.method == "GET": + form = WorkerContactForm() + worker = assignment.worker + context = { + "form": form, + "worker": worker + } + return(request, "contact_worker_modal.html", context) + elif request.method == "POST": + form = WorkerContactForm(request.POST) + if form.is_valid(): + subject = form.cleaned_data['subject'] + message = form.cleaned_data['message'] + assignment.contact_worker(subject, message) + return manage_hit(assignment.hit.battery.id, assignment.hit.id) + else: + return HttpResponseNotAllowed() + # Expire a hit @login_required def expire_hit(request, hid): From 7e31470b3dac3b0f7058e41257f45cf1ae428b0d Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 29 Jun 2016 16:48:03 -0700 Subject: [PATCH 031/142] temporary contact worker button in manage hit view, still missing modal support, and a proper url route --- expdj/apps/turk/templates/turk/manage_hit.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/expdj/apps/turk/templates/turk/manage_hit.html b/expdj/apps/turk/templates/turk/manage_hit.html index d6e7fde..5b845ff 100644 --- a/expdj/apps/turk/templates/turk/manage_hit.html +++ b/expdj/apps/turk/templates/turk/manage_hit.html @@ -158,6 +158,7 @@

    Approved

    Worker ID Status Accept TimeContact
    {{ assignment.worker }} {{ assignment.status }} {{ assignment.accept_time | localize }} Contact Worker
    + {% crispy form %} +
    + + + + + From 187c82fe8074fcd680286286c9dbdc6d82220b9a Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 30 Jun 2016 15:44:49 -0700 Subject: [PATCH 033/142] added back in an import that was accidentally removed --- expdj/apps/experiments/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index f16be6e..9fa0e38 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -12,6 +12,7 @@ from expfactory.battery import get_load_static, get_experiment_run from expfactory.survey import generate_survey from expfactory.experiment import load_experiment +from expfactory.views import embed_experiment from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied, ValidationError From 23c3d9db58811e3a749adf004f7eb92fa8a8e3db Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 15:32:10 -0700 Subject: [PATCH 034/142] fix import issues in turk views. Added url for contact worker. Added code to load contact worker view in manage_hit template --- .../apps/turk/templates/turk/manage_hit.html | 9 ++++- expdj/apps/turk/urls.py | 39 ++++++++++++++----- expdj/apps/turk/utils.py | 18 ++++++--- expdj/apps/turk/views.py | 5 +-- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/expdj/apps/turk/templates/turk/manage_hit.html b/expdj/apps/turk/templates/turk/manage_hit.html index 5b845ff..58e3d46 100644 --- a/expdj/apps/turk/templates/turk/manage_hit.html +++ b/expdj/apps/turk/templates/turk/manage_hit.html @@ -168,7 +168,7 @@

    Approved

    {{ assignment.worker }} {{ assignment.status }} {{ assignment.accept_time | localize }} - Contact Worker + Contact Worker {% endfor %} @@ -177,6 +177,8 @@

    Approved

    {% endif %} + {% endblock %} @@ -192,6 +194,11 @@

    Approved

    $('.collapse').collapse('hide'); }) +$('.contact_worker').on("click", function(e) { + e.preventDefault(); + $('#contact_modal').modal("show").load(this.href); +}); + }) {% endblock %} diff --git a/expdj/apps/turk/urls.py b/expdj/apps/turk/urls.py index 1bdf124..73c84d8 100644 --- a/expdj/apps/turk/urls.py +++ b/expdj/apps/turk/urls.py @@ -1,6 +1,6 @@ -from expdj.apps.turk.views import edit_hit, delete_hit, expire_hit, preview_hit, \ -serve_hit, multiple_new_hit, end_assignment, finished_view, not_consent_view, \ -survey_submit, manage_hit +from expdj.apps.turk.views import (edit_hit, delete_hit, expire_hit, + preview_hit, serve_hit, multiple_new_hit, end_assignment, finished_view, + not_consent_view, survey_submit, manage_hit, contact_worker) from expdj.apps.experiments.views import sync from django.views.generic.base import TemplateView from django.conf.urls import patterns, url @@ -8,9 +8,21 @@ urlpatterns = patterns('', # HITS url(r'^hits/(?P\d+|[A-Z]{8})/new$',edit_hit,name='new_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/manage$',manage_hit,name='manage_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/multiple$',multiple_new_hit,name='multiple_new_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/edit$',edit_hit,name='edit_hit'), + url( + r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/manage$', + manage_hit, + name='manage_hit' + ), + url( + r'^hits/(?P\d+|[A-Z]{8})/multiple$', + multiple_new_hit, + name='multiple_new_hit' + ), + url( + r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/edit$', + edit_hit, + name='edit_hit' + ), url(r'^hits/(?P\d+|[A-Z]{8})/delete$',delete_hit,name='delete_hit'), url(r'^hits/(?P\d+|[A-Z]{8})/expire$',expire_hit,name='expire_hit'), @@ -18,9 +30,18 @@ url(r'^accept/(?P\d+|[A-Z]{8})',serve_hit,name='serve_hit'), url(r'^turk/(?P\d+|[A-Z]{8})',preview_hit,name='preview_hit'), url(r'^turk/preview',not_consent_view,name='not_consent_view'), - url(r'^turk/end/(?P\d+|[A-Z]{8})',end_assignment,name='end_assignment'), - url(r'^surveys/(?P\d+|[A-Z]{8})/(?P[A-Za-z0-9]{30})/submit$',survey_submit,name='survey_submit'), + url( + r'^turk/end/(?P\d+|[A-Z]{8})', + end_assignment, + name='end_assignment' + ), + url( + r'^surveys/(?P\d+|[A-Z]{8})/(?P[A-Za-z0-9]{30})/submit$', + survey_submit, + name='survey_submit' + ), url(r'^sync/(?P\d+|[A-Z]{8})/$',sync,name='sync_data'), url(r'^sync/$',sync,name='sync_data'), - url(r'^finished$', finished_view, name="finished_view") + url(r'^finished$', finished_view, name="finished_view"), + url(r'^worker/contact/(?P\d+)',contact_worker,name='contact_worker') ) diff --git a/expdj/apps/turk/utils.py b/expdj/apps/turk/utils.py index ad525ee..c8902f9 100644 --- a/expdj/apps/turk/utils.py +++ b/expdj/apps/turk/utils.py @@ -1,16 +1,19 @@ -from expdj.apps.experiments.models import Experiment -from boto.mturk.connection import MTurkConnection -from expdj.settings import BASE_DIR, MTURK_ALLOW -from boto.mturk.question import ExternalQuestion -from boto.mturk.price import Price import ConfigParser import datetime -import pandas import json import os +from boto.mturk.connection import MTurkConnection +from boto.mturk.price import Price +from boto.mturk.question import ExternalQuestion +import pandas + from django.conf import settings +from expdj.apps.experiments.models import Experiment +from expdj.settings import BASE_DIR, MTURK_ALLOW + + # RESULTS UTILS def to_dict(input_ordered_dict): @@ -132,3 +135,6 @@ def get_time_difference(d1,d2,format='%Y-%m-%d %H:%M:%S'): if isinstance(d2,str): d2 = datetime.datetime.strptime(d2, format) return (d2 - d1).total_seconds() / 60 + + + diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 7bd9145..335689c 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -15,15 +15,14 @@ from django.utils import timezone from django.views.decorators.csrf import ensure_csrf_cookie -from expdj.apps.experiments.models import (Assignment, Battery, - ExperimentTemplate) +from expdj.apps.experiments.models import (Battery, ExperimentTemplate) from expdj.apps.experiments.views import (check_battery_edit_permission, check_mturk_access, get_battery_intro, deploy_battery) from expdj.apps.experiments.utils import get_experiment_type, select_experiments from expdj.apps.turk.forms import HITForm, WorkerContactForm from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker from expdj.apps.turk.tasks import (assign_experiment_credit, - check_battery_dependencies, get_unique_experiments) + get_unique_experiments) from expdj.apps.turk.utils import (get_connection, get_host, get_worker_url, get_worker_experiments) from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT From 786ef3b47620a662c90e63175be4802b06cb2c60 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 15:49:37 -0700 Subject: [PATCH 035/142] fixed a couple sytax issues --- expdj/apps/turk/forms.py | 2 +- expdj/apps/turk/views.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/expdj/apps/turk/forms.py b/expdj/apps/turk/forms.py index d5674a3..4f7ccf3 100644 --- a/expdj/apps/turk/forms.py +++ b/expdj/apps/turk/forms.py @@ -37,6 +37,6 @@ class WorkerContactForm(forms.Form): message = forms.CharField(label="Message") def __init__(self, *args, **kwargs): - super(ContactForm, self).__init__(*args, **kwargs) + super(WorkerContactForm, self).__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.add_input(Submit("submit", "Send")) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 335689c..628af15 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -342,7 +342,7 @@ def contact_worker(request, aid): if mturk_permission == False: return HttpResponseForbidden() - assignment = Assignments.objects.get(aid) + assignment = Assignment.objects.get(id=aid) if request.method == "GET": form = WorkerContactForm() worker = assignment.worker @@ -350,7 +350,7 @@ def contact_worker(request, aid): "form": form, "worker": worker } - return(request, "contact_worker_modal.html", context) + return render(request, "turk/contact_worker_modal.html", context) elif request.method == "POST": form = WorkerContactForm(request.POST) if form.is_valid(): From 6b2f4cb428e1d2845ae9810de3d4e0a039bc1e5e Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 15:51:36 -0700 Subject: [PATCH 036/142] forgot to pass request to manage_hit when redirecting in contact worker --- expdj/apps/turk/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 628af15..317c98d 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -357,7 +357,8 @@ def contact_worker(request, aid): subject = form.cleaned_data['subject'] message = form.cleaned_data['message'] assignment.contact_worker(subject, message) - return manage_hit(assignment.hit.battery.id, assignment.hit.id) + return manage_hit(request, assignment.hit.battery.id, + assignment.hit.id) else: return HttpResponseNotAllowed() From 81e3cea70bf1d7c6d55e1b01353f56f657cd14c7 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 16:00:21 -0700 Subject: [PATCH 037/142] added missing form tags for contact worker modal --- expdj/apps/turk/templates/turk/contact_worker_modal.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expdj/apps/turk/templates/turk/contact_worker_modal.html b/expdj/apps/turk/templates/turk/contact_worker_modal.html index 8fdb15e..7d6f9ad 100644 --- a/expdj/apps/turk/templates/turk/contact_worker_modal.html +++ b/expdj/apps/turk/templates/turk/contact_worker_modal.html @@ -7,9 +7,9 @@
{% endif %} + Return Hit diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index bfc6002..7498884 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -132,7 +132,8 @@ def serve_hit(request,hid): # Get Experiment Factory objects for each worker = get_worker(aws["worker_id"]) - check_battery_response = check_battery_view(battery, aws["worker_id"]) + check_battery_response = check_battery_view(battery, aws["worker_id"], + hit) if (check_battery_response): return check_battery_response @@ -389,13 +390,24 @@ def get_flagged_questions(number=None): return questions return choice(questions,int(number)) -def check_battery_view(battery, worker_id): +def check_battery_view(battery, worker_id, hit): missing_batteries, blocking_batteries = check_battery_dependencies(battery, worker_id) if missing_batteries or blocking_batteries: + return_hit_url = ( + "{host}/mturk/return?requesterId={worker_id}&hitId={hit_id}&" + "groupId={group_id}&canAccept=&externalHit=true" + ) + return_hit_url.format({ + 'host': get_host(), + 'worker_id': worker_id, + 'hit_id': hit.mturk_id, + 'group_id': hit.hit_type_id, + }) return render_to_response( "experiments/battery_requirements_not_met.html", context={'missing_batteries': missing_batteries, - 'blocking_batteries': blocking_batteries} + 'blocking_batteries': blocking_batteries, + 'return_hit_url': return_hit_url} ) else: return None From a6a53a50c553c38f235bd515083baa9927c54aac Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 6 Jul 2016 20:45:10 -0400 Subject: [PATCH 043/142] fix logic issue in get_worker_url, updated code that generates return hit url --- expdj/apps/turk/utils.py | 2 +- expdj/apps/turk/views.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/expdj/apps/turk/utils.py b/expdj/apps/turk/utils.py index ad525ee..6ec8592 100644 --- a/expdj/apps/turk/utils.py +++ b/expdj/apps/turk/utils.py @@ -75,7 +75,7 @@ def is_sandbox(): def get_worker_url(): """Get proper URL depending upon sandbox settings""" - if is_sandbox(): + if settings.MTURK_ALLOW == False: return SANDBOX_WORKER_URL else: return PRODUCTION_WORKER_URL diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 7498884..0c9ea06 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -397,12 +397,16 @@ def check_battery_view(battery, worker_id, hit): "{host}/mturk/return?requesterId={worker_id}&hitId={hit_id}&" "groupId={group_id}&canAccept=&externalHit=true" ) - return_hit_url.format({ - 'host': get_host(), + worker_url = get_worker_url() + format_data = { + 'host': worker_url, 'worker_id': worker_id, 'hit_id': hit.mturk_id, 'group_id': hit.hit_type_id, - }) + } + print(format_data) + return_hit_url = return_hit_url.format(**format_data) + print(return_hit_url) return render_to_response( "experiments/battery_requirements_not_met.html", context={'missing_batteries': missing_batteries, From 94063e31a68223f9d29695d89b229b46e85f1c3e Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 6 Jul 2016 18:10:40 -0700 Subject: [PATCH 044/142] remove return_hit_url code from this branch. Moved requiments not met template to a turk template directory --- .../turk}/battery_requirements_not_met.html | 0 expdj/apps/turk/views.py | 24 ++++--------------- 2 files changed, 4 insertions(+), 20 deletions(-) rename expdj/apps/{experiments/templates/experiments => turk/templates/turk}/battery_requirements_not_met.html (100%) diff --git a/expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html b/expdj/apps/turk/templates/turk/battery_requirements_not_met.html similarity index 100% rename from expdj/apps/experiments/templates/experiments/battery_requirements_not_met.html rename to expdj/apps/turk/templates/turk/battery_requirements_not_met.html diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 0c9ea06..c8e337c 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -132,8 +132,7 @@ def serve_hit(request,hid): # Get Experiment Factory objects for each worker = get_worker(aws["worker_id"]) - check_battery_response = check_battery_view(battery, aws["worker_id"], - hit) + check_battery_response = check_battery_view(battery, aws["worker_id"]) if (check_battery_response): return check_battery_response @@ -390,28 +389,13 @@ def get_flagged_questions(number=None): return questions return choice(questions,int(number)) -def check_battery_view(battery, worker_id, hit): +def check_battery_view(battery, worker_id): missing_batteries, blocking_batteries = check_battery_dependencies(battery, worker_id) if missing_batteries or blocking_batteries: - return_hit_url = ( - "{host}/mturk/return?requesterId={worker_id}&hitId={hit_id}&" - "groupId={group_id}&canAccept=&externalHit=true" - ) - worker_url = get_worker_url() - format_data = { - 'host': worker_url, - 'worker_id': worker_id, - 'hit_id': hit.mturk_id, - 'group_id': hit.hit_type_id, - } - print(format_data) - return_hit_url = return_hit_url.format(**format_data) - print(return_hit_url) return render_to_response( - "experiments/battery_requirements_not_met.html", + "turk/battery_requirements_not_met.html", context={'missing_batteries': missing_batteries, - 'blocking_batteries': blocking_batteries, - 'return_hit_url': return_hit_url} + 'blocking_batteries': blocking_batteries} ) else: return None From d54bcf75332d8ed8a1b5feac77fd9c1f1175a4ed Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 7 Jul 2016 09:46:09 -0700 Subject: [PATCH 045/142] remove return hit link from reqs not met template --- expdj/apps/turk/templates/turk/battery_requirements_not_met.html | 1 - 1 file changed, 1 deletion(-) diff --git a/expdj/apps/turk/templates/turk/battery_requirements_not_met.html b/expdj/apps/turk/templates/turk/battery_requirements_not_met.html index bad62a1..4f5fde2 100644 --- a/expdj/apps/turk/templates/turk/battery_requirements_not_met.html +++ b/expdj/apps/turk/templates/turk/battery_requirements_not_met.html @@ -44,7 +44,6 @@

Some Requirements Have Not Been Met.

{% endfor %} {% endif %} - Return Hit From 60e81a91429f61d6fd5dd8bff181d4f012db37a5 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 11 Jul 2016 14:39:19 -0500 Subject: [PATCH 046/142] added total number of experiments to be displayed next to experiments left in inter-expirement template --- expdj/apps/experiments/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index 9d4ee25..125257e 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -479,8 +479,9 @@ def deploy_battery(deployment, battery, experiment_type, context, task_list, runcode = runcode.replace("{{result.id}}",str(result.id)) runcode = runcode.replace("{{next_page}}",next_page) if experiments_left: - expleft_msg = "

Experiments left in battery: {0:d}

" - expleft_msg = expleft_msg.format(experiments_left) + total_experiments = battery.number_of_experiments + expleft_msg = "

Experiments left in battery {0:d} out of {1:d}

" + expleft_msg = expleft_msg.format(experiments_left, total_experiments) runcode = runcode.replace("

", expleft_msg) elif experiment_type in ["games"]: experiment = load_experiment(experiment_folders[0]) From 46df995442e215f77b99c9963b7b0b578c9412ae Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Jun 2016 15:17:52 -0700 Subject: [PATCH 047/142] add number of experiments left message to code that is executed client side between experiments in a battery --- expdj/apps/experiments/views.py | 34 +++++++++++++++++++++------------ expdj/apps/turk/views.py | 28 +++++++++++++++------------ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index 9fa0e38..677f67e 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -422,7 +422,8 @@ def serve_battery(request,bid,userid=None): # Does the worker have experiments remaining? uncompleted_experiments = get_worker_experiments(worker,battery) - if len(uncompleted_experiments) == 0: + experiments_left = len(uncompleted_experiments) + if experiments_left == 0: # Thank you for your participation - no more experiments! return render_to_response("turk/worker_sorry.html") @@ -442,22 +443,27 @@ def serve_battery(request,bid,userid=None): "uniqueId":result.id} # If this is the last experiment, the finish button will link to a thank you page. - if len(uncompleted_experiments) == 1: + if experiments_left == 1: next_page = "/finished" # Determine template name based on template_type template = "%s/serve_battery.html" %(experiment_type) - return deploy_battery(deployment="docker-local", - battery=battery, - experiment_type=experiment_type, - context=context, - task_list=task_list, - template=template, - next_page=next_page, - result=result) - -def deploy_battery(deployment,battery,experiment_type,context,task_list,template,result,next_page=None,last_experiment=False): + return deploy_battery( + deployment="docker-local", + battery=battery, + experiment_type=experiment_type, + context=context, + task_list=task_list, + template=template, + next_page=next_page, + result=result, + experiments_left=experiments_left-1 + ) + +def deploy_battery(deployment, battery, experiment_type, context, task_list, + template, result, next_page=None, last_experiment=False, + experiments_left=None): '''deploy_battery is a general function for returning the final view to deploy a battery, either local or MTurk :param deployment: either "docker-mturk" or "docker-local" :param battery: models.Battery object @@ -494,6 +500,10 @@ def deploy_battery(deployment,battery,experiment_type,context,task_list,template if result != None: runcode = runcode.replace("{{result.id}}",str(result.id)) runcode = runcode.replace("{{next_page}}",next_page) + if experiments_left: + expleft_msg = "

Experiments left in battery: {0:d}

" + expleft_msg = expleft_msg.format(experiments_left) + runcode = runcode.replace("

", expleft_msg) elif experiment_type in ["games"]: experiment = load_experiment(experiment_folders[0]) runcode = experiment[0]["deployment_variables"]["run"] diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index c8e337c..d9ded31 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -161,13 +161,14 @@ def serve_hit(request,hid): # Does the worker have experiments remaining for the hit? uncompleted_experiments = get_worker_experiments(worker,hit.battery) - if len(uncompleted_experiments) == 0: + experiments_left = len(uncompleted_experiments) + if experiments_left == 0: # Thank you for your participation - no more experiments! return render_to_response("turk/worker_sorry.html") # if it's the last experiment, we will submit the result to amazon (only for surveys) last_experiment = False - if len(uncompleted_experiments) == 1: + if experiments_left == 1: last_experiment = True task_list = select_experiments(battery,uncompleted_experiments) @@ -189,18 +190,21 @@ def serve_hit(request,hid): aws["uniqueId"] = result.id # If this is the last experiment, the finish button will link to a thank you page. - if len(uncompleted_experiments) == 1: + if experiments_left == 1: next_page = "/finished" - return deploy_battery(deployment="docker-mturk", - battery=battery, - experiment_type=experiment_type, - context=aws, - task_list=task_list, - template=template, - next_page=None, - result=result, - last_experiment=last_experiment) + return deploy_battery( + deployment="docker-mturk", + battery=battery, + experiment_type=experiment_type, + context=aws, + task_list=task_list, + template=template, + next_page=None, + result=result, + last_experiment=last_experiment, + experiments_left=experiments_left-1 + ) else: return render_to_response("turk/error_sorry.html") From 10fd353e1443d8991009463128954e2aae97d11c Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Jun 2016 15:28:45 -0700 Subject: [PATCH 048/142] added doc string for new argument to deploy_battery, should fix #156 --- expdj/apps/experiments/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index 677f67e..be18bc6 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -474,6 +474,7 @@ def deploy_battery(deployment, battery, experiment_type, context, task_list, :param template: html template to render :param result: the result object, turk.models.Result :param last_experiment: boolean if true will redirect the user to a page to submit the result (for surveys) + :param experiments_left: integer indicating how many experiments are left in battery. ''' if next_page == None: next_page = "javascript:window.location.reload();" From a4c4eb479df9cd16d66a41b672d2d1bf2dd66467 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 11 Jul 2016 14:39:19 -0500 Subject: [PATCH 049/142] added total number of experiments to be displayed next to experiments left in inter-expirement template --- expdj/apps/experiments/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/expdj/apps/experiments/views.py b/expdj/apps/experiments/views.py index be18bc6..15dd047 100644 --- a/expdj/apps/experiments/views.py +++ b/expdj/apps/experiments/views.py @@ -502,8 +502,9 @@ def deploy_battery(deployment, battery, experiment_type, context, task_list, runcode = runcode.replace("{{result.id}}",str(result.id)) runcode = runcode.replace("{{next_page}}",next_page) if experiments_left: - expleft_msg = "

Experiments left in battery: {0:d}

" - expleft_msg = expleft_msg.format(experiments_left) + total_experiments = battery.number_of_experiments + expleft_msg = "

Experiments left in battery {0:d} out of {1:d}

" + expleft_msg = expleft_msg.format(experiments_left, total_experiments) runcode = runcode.replace("

", expleft_msg) elif experiment_type in ["games"]: experiment = load_experiment(experiment_folders[0]) From eb0375b6fc64112e183b2276da42784877024968 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 29 Jun 2016 16:45:56 -0700 Subject: [PATCH 050/142] Add form to contact workers with --- expdj/apps/turk/forms.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/expdj/apps/turk/forms.py b/expdj/apps/turk/forms.py index eb35dad..d5674a3 100644 --- a/expdj/apps/turk/forms.py +++ b/expdj/apps/turk/forms.py @@ -31,3 +31,12 @@ def __init__(self, *args, **kwargs): self.helper.layout = Layout() tab_holder = TabHolder() self.helper.add_input(Submit("submit", "Save")) + +class WorkerContactForm(forms.Form): + subject = forms.CharField(label="Subject") + message = forms.CharField(label="Message") + + def __init__(self, *args, **kwargs): + super(ContactForm, self).__init__(*args, **kwargs) + self.helper = FormHelper(self) + self.helper.add_input(Submit("submit", "Send")) From d02ad1f5da7f84226ffbc19d443131eb76573a34 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 29 Jun 2016 16:47:04 -0700 Subject: [PATCH 051/142] add contact worker function to assignment model, this model has access to all the information we need to generate connections and send information to workers. Add contact worker view to handle creating and processing the form --- expdj/apps/turk/models.py | 13 ++++++++++++- expdj/apps/turk/views.py | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/expdj/apps/turk/models.py b/expdj/apps/turk/models.py index 5319836..2d54ce4 100644 --- a/expdj/apps/turk/models.py +++ b/expdj/apps/turk/models.py @@ -407,7 +407,6 @@ class Assignment(models.Model): (True, 'Completed')), default=False,verbose_name="participant completed the entire assignment") - def create(self): init_connection_callback(sender=self.hit) @@ -480,6 +479,18 @@ def update(self, mturk_assignment=None, hit=None): self.save() + def contact_worker(self, subject, msg): + '''contact_worker uses the boto function notify_workers to send a + message a specific worker. + :subject: Subject of the message to send. + :msg: Contents of the message to send. + ''' + self.hit.generate_connection() + try: + self.hit.connection.notify_workers(self.worker.id, subject, msg) + except MTurkRequestError: + pass + def __unicode__(self): return self.mturk_id diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index c8e337c..9c6aae0 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -9,16 +9,18 @@ from django.contrib.auth.decorators import login_required from django.core.management.base import BaseCommand -from django.http.response import HttpResponseRedirect, HttpResponseForbidden, HttpResponse, Http404 +from django.http.response import (HttpResponseRedirect, HttpResponseForbidden, + HttpResponse, Http404, HttpResponseNotAllowed) from django.shortcuts import get_object_or_404, render_to_response, render, redirect from django.utils import timezone from django.views.decorators.csrf import ensure_csrf_cookie -from expdj.apps.experiments.models import Battery, ExperimentTemplate -from expdj.apps.experiments.views import check_battery_edit_permission, check_mturk_access, \ -get_battery_intro, deploy_battery +from expdj.apps.experiments.models import (Assignment, Battery, + ExperimentTemplate) +from expdj.apps.experiments.views import (check_battery_edit_permission, + check_mturk_access, get_battery_intro, deploy_battery) from expdj.apps.experiments.utils import get_experiment_type, select_experiments -from expdj.apps.turk.forms import HITForm +from expdj.apps.turk.forms import HITForm, WorkerContactForm from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker from expdj.apps.turk.tasks import (assign_experiment_credit, check_battery_dependencies, get_unique_experiments) @@ -339,6 +341,32 @@ def edit_hit(request, bid, hid=None): else: return HttpResponseForbidden() +@login_required +def contact_worker(request, aid): + mturk_permission = check_mturk_access(request) + + if mturk_permission == False: + return HttpResponseForbidden() + + assignment = Assignments.objects.get(aid) + if request.method == "GET": + form = WorkerContactForm() + worker = assignment.worker + context = { + "form": form, + "worker": worker + } + return(request, "contact_worker_modal.html", context) + elif request.method == "POST": + form = WorkerContactForm(request.POST) + if form.is_valid(): + subject = form.cleaned_data['subject'] + message = form.cleaned_data['message'] + assignment.contact_worker(subject, message) + return manage_hit(assignment.hit.battery.id, assignment.hit.id) + else: + return HttpResponseNotAllowed() + # Expire a hit @login_required def expire_hit(request, hid): From f0b684dbdee342774df219543f8813969fdeda30 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 29 Jun 2016 16:48:03 -0700 Subject: [PATCH 052/142] temporary contact worker button in manage hit view, still missing modal support, and a proper url route --- expdj/apps/turk/templates/turk/manage_hit.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/expdj/apps/turk/templates/turk/manage_hit.html b/expdj/apps/turk/templates/turk/manage_hit.html index d6e7fde..5b845ff 100644 --- a/expdj/apps/turk/templates/turk/manage_hit.html +++ b/expdj/apps/turk/templates/turk/manage_hit.html @@ -158,6 +158,7 @@

Approved

Worker ID Status Accept Time + Contact @@ -167,6 +168,7 @@

Approved

{{ assignment.worker }} {{ assignment.status }} {{ assignment.accept_time | localize }} + Contact Worker {% endfor %} From 9e1a47f728426aaff3d1050b28170c9c38446a7e Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 30 Jun 2016 15:10:04 -0700 Subject: [PATCH 053/142] added template for contact worker form --- .../templates/turk/contact_worker_modal.html | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 expdj/apps/turk/templates/turk/contact_worker_modal.html diff --git a/expdj/apps/turk/templates/turk/contact_worker_modal.html b/expdj/apps/turk/templates/turk/contact_worker_modal.html new file mode 100644 index 0000000..8fdb15e --- /dev/null +++ b/expdj/apps/turk/templates/turk/contact_worker_modal.html @@ -0,0 +1,19 @@ +{% load crispy_forms_tags %} + From 761422c730e83ed02a766eaeff30cdf5768df2ec Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 15:32:10 -0700 Subject: [PATCH 054/142] fix import issues in turk views. Added url for contact worker. Added code to load contact worker view in manage_hit template --- .../apps/turk/templates/turk/manage_hit.html | 9 ++++- expdj/apps/turk/urls.py | 39 ++++++++++++++----- expdj/apps/turk/utils.py | 18 ++++++--- expdj/apps/turk/views.py | 5 +-- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/expdj/apps/turk/templates/turk/manage_hit.html b/expdj/apps/turk/templates/turk/manage_hit.html index 5b845ff..58e3d46 100644 --- a/expdj/apps/turk/templates/turk/manage_hit.html +++ b/expdj/apps/turk/templates/turk/manage_hit.html @@ -168,7 +168,7 @@

Approved

{{ assignment.worker }} {{ assignment.status }} {{ assignment.accept_time | localize }} - Contact Worker + Contact Worker {% endfor %} @@ -177,6 +177,8 @@

Approved

{% endif %} + {% endblock %} @@ -192,6 +194,11 @@

Approved

$('.collapse').collapse('hide'); }) +$('.contact_worker').on("click", function(e) { + e.preventDefault(); + $('#contact_modal').modal("show").load(this.href); +}); + }) {% endblock %} diff --git a/expdj/apps/turk/urls.py b/expdj/apps/turk/urls.py index 1bdf124..73c84d8 100644 --- a/expdj/apps/turk/urls.py +++ b/expdj/apps/turk/urls.py @@ -1,6 +1,6 @@ -from expdj.apps.turk.views import edit_hit, delete_hit, expire_hit, preview_hit, \ -serve_hit, multiple_new_hit, end_assignment, finished_view, not_consent_view, \ -survey_submit, manage_hit +from expdj.apps.turk.views import (edit_hit, delete_hit, expire_hit, + preview_hit, serve_hit, multiple_new_hit, end_assignment, finished_view, + not_consent_view, survey_submit, manage_hit, contact_worker) from expdj.apps.experiments.views import sync from django.views.generic.base import TemplateView from django.conf.urls import patterns, url @@ -8,9 +8,21 @@ urlpatterns = patterns('', # HITS url(r'^hits/(?P\d+|[A-Z]{8})/new$',edit_hit,name='new_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/manage$',manage_hit,name='manage_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/multiple$',multiple_new_hit,name='multiple_new_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/edit$',edit_hit,name='edit_hit'), + url( + r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/manage$', + manage_hit, + name='manage_hit' + ), + url( + r'^hits/(?P\d+|[A-Z]{8})/multiple$', + multiple_new_hit, + name='multiple_new_hit' + ), + url( + r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/edit$', + edit_hit, + name='edit_hit' + ), url(r'^hits/(?P\d+|[A-Z]{8})/delete$',delete_hit,name='delete_hit'), url(r'^hits/(?P\d+|[A-Z]{8})/expire$',expire_hit,name='expire_hit'), @@ -18,9 +30,18 @@ url(r'^accept/(?P\d+|[A-Z]{8})',serve_hit,name='serve_hit'), url(r'^turk/(?P\d+|[A-Z]{8})',preview_hit,name='preview_hit'), url(r'^turk/preview',not_consent_view,name='not_consent_view'), - url(r'^turk/end/(?P\d+|[A-Z]{8})',end_assignment,name='end_assignment'), - url(r'^surveys/(?P\d+|[A-Z]{8})/(?P[A-Za-z0-9]{30})/submit$',survey_submit,name='survey_submit'), + url( + r'^turk/end/(?P\d+|[A-Z]{8})', + end_assignment, + name='end_assignment' + ), + url( + r'^surveys/(?P\d+|[A-Z]{8})/(?P[A-Za-z0-9]{30})/submit$', + survey_submit, + name='survey_submit' + ), url(r'^sync/(?P\d+|[A-Z]{8})/$',sync,name='sync_data'), url(r'^sync/$',sync,name='sync_data'), - url(r'^finished$', finished_view, name="finished_view") + url(r'^finished$', finished_view, name="finished_view"), + url(r'^worker/contact/(?P\d+)',contact_worker,name='contact_worker') ) diff --git a/expdj/apps/turk/utils.py b/expdj/apps/turk/utils.py index 6ec8592..1f9b803 100644 --- a/expdj/apps/turk/utils.py +++ b/expdj/apps/turk/utils.py @@ -1,16 +1,19 @@ -from expdj.apps.experiments.models import Experiment -from boto.mturk.connection import MTurkConnection -from expdj.settings import BASE_DIR, MTURK_ALLOW -from boto.mturk.question import ExternalQuestion -from boto.mturk.price import Price import ConfigParser import datetime -import pandas import json import os +from boto.mturk.connection import MTurkConnection +from boto.mturk.price import Price +from boto.mturk.question import ExternalQuestion +import pandas + from django.conf import settings +from expdj.apps.experiments.models import Experiment +from expdj.settings import BASE_DIR, MTURK_ALLOW + + # RESULTS UTILS def to_dict(input_ordered_dict): @@ -132,3 +135,6 @@ def get_time_difference(d1,d2,format='%Y-%m-%d %H:%M:%S'): if isinstance(d2,str): d2 = datetime.datetime.strptime(d2, format) return (d2 - d1).total_seconds() / 60 + + + diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 9c6aae0..76d3e3e 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -15,15 +15,14 @@ from django.utils import timezone from django.views.decorators.csrf import ensure_csrf_cookie -from expdj.apps.experiments.models import (Assignment, Battery, - ExperimentTemplate) +from expdj.apps.experiments.models import (Battery, ExperimentTemplate) from expdj.apps.experiments.views import (check_battery_edit_permission, check_mturk_access, get_battery_intro, deploy_battery) from expdj.apps.experiments.utils import get_experiment_type, select_experiments from expdj.apps.turk.forms import HITForm, WorkerContactForm from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker from expdj.apps.turk.tasks import (assign_experiment_credit, - check_battery_dependencies, get_unique_experiments) + get_unique_experiments) from expdj.apps.turk.utils import (get_connection, get_host, get_worker_url, get_worker_experiments) from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT From 8b60c7cecbfdb71ebf19ed525e519b0c94bb6311 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 15:49:37 -0700 Subject: [PATCH 055/142] fixed a couple sytax issues --- expdj/apps/turk/forms.py | 2 +- expdj/apps/turk/views.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/expdj/apps/turk/forms.py b/expdj/apps/turk/forms.py index d5674a3..4f7ccf3 100644 --- a/expdj/apps/turk/forms.py +++ b/expdj/apps/turk/forms.py @@ -37,6 +37,6 @@ class WorkerContactForm(forms.Form): message = forms.CharField(label="Message") def __init__(self, *args, **kwargs): - super(ContactForm, self).__init__(*args, **kwargs) + super(WorkerContactForm, self).__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.add_input(Submit("submit", "Send")) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 76d3e3e..4f2f722 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -347,7 +347,7 @@ def contact_worker(request, aid): if mturk_permission == False: return HttpResponseForbidden() - assignment = Assignments.objects.get(aid) + assignment = Assignment.objects.get(id=aid) if request.method == "GET": form = WorkerContactForm() worker = assignment.worker @@ -355,7 +355,7 @@ def contact_worker(request, aid): "form": form, "worker": worker } - return(request, "contact_worker_modal.html", context) + return render(request, "turk/contact_worker_modal.html", context) elif request.method == "POST": form = WorkerContactForm(request.POST) if form.is_valid(): From 4e5565b94aff6b8b9c0d256d3bc577d5539a9696 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 15:51:36 -0700 Subject: [PATCH 056/142] forgot to pass request to manage_hit when redirecting in contact worker --- expdj/apps/turk/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 4f2f722..067014a 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -362,7 +362,8 @@ def contact_worker(request, aid): subject = form.cleaned_data['subject'] message = form.cleaned_data['message'] assignment.contact_worker(subject, message) - return manage_hit(assignment.hit.battery.id, assignment.hit.id) + return manage_hit(request, assignment.hit.battery.id, + assignment.hit.id) else: return HttpResponseNotAllowed() From e06b802e45f24cd0ef60ad4f5abde490f63066d1 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 16:00:21 -0700 Subject: [PATCH 057/142] added missing form tags for contact worker modal --- expdj/apps/turk/templates/turk/contact_worker_modal.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expdj/apps/turk/templates/turk/contact_worker_modal.html b/expdj/apps/turk/templates/turk/contact_worker_modal.html index 8fdb15e..7d6f9ad 100644 --- a/expdj/apps/turk/templates/turk/contact_worker_modal.html +++ b/expdj/apps/turk/templates/turk/contact_worker_modal.html @@ -7,9 +7,9 @@ {% endif %} + {% endblock %} @@ -192,6 +194,11 @@

Approved

$('.collapse').collapse('hide'); }) +$('.contact_worker').on("click", function(e) { + e.preventDefault(); + $('#contact_modal').modal("show").load(this.href); +}); + }) {% endblock %} diff --git a/expdj/apps/turk/urls.py b/expdj/apps/turk/urls.py index 1bdf124..73c84d8 100644 --- a/expdj/apps/turk/urls.py +++ b/expdj/apps/turk/urls.py @@ -1,6 +1,6 @@ -from expdj.apps.turk.views import edit_hit, delete_hit, expire_hit, preview_hit, \ -serve_hit, multiple_new_hit, end_assignment, finished_view, not_consent_view, \ -survey_submit, manage_hit +from expdj.apps.turk.views import (edit_hit, delete_hit, expire_hit, + preview_hit, serve_hit, multiple_new_hit, end_assignment, finished_view, + not_consent_view, survey_submit, manage_hit, contact_worker) from expdj.apps.experiments.views import sync from django.views.generic.base import TemplateView from django.conf.urls import patterns, url @@ -8,9 +8,21 @@ urlpatterns = patterns('', # HITS url(r'^hits/(?P\d+|[A-Z]{8})/new$',edit_hit,name='new_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/manage$',manage_hit,name='manage_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/multiple$',multiple_new_hit,name='multiple_new_hit'), - url(r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/edit$',edit_hit,name='edit_hit'), + url( + r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/manage$', + manage_hit, + name='manage_hit' + ), + url( + r'^hits/(?P\d+|[A-Z]{8})/multiple$', + multiple_new_hit, + name='multiple_new_hit' + ), + url( + r'^hits/(?P\d+|[A-Z]{8})/(?P\d+|[A-Z]{8})/edit$', + edit_hit, + name='edit_hit' + ), url(r'^hits/(?P\d+|[A-Z]{8})/delete$',delete_hit,name='delete_hit'), url(r'^hits/(?P\d+|[A-Z]{8})/expire$',expire_hit,name='expire_hit'), @@ -18,9 +30,18 @@ url(r'^accept/(?P\d+|[A-Z]{8})',serve_hit,name='serve_hit'), url(r'^turk/(?P\d+|[A-Z]{8})',preview_hit,name='preview_hit'), url(r'^turk/preview',not_consent_view,name='not_consent_view'), - url(r'^turk/end/(?P\d+|[A-Z]{8})',end_assignment,name='end_assignment'), - url(r'^surveys/(?P\d+|[A-Z]{8})/(?P[A-Za-z0-9]{30})/submit$',survey_submit,name='survey_submit'), + url( + r'^turk/end/(?P\d+|[A-Z]{8})', + end_assignment, + name='end_assignment' + ), + url( + r'^surveys/(?P\d+|[A-Z]{8})/(?P[A-Za-z0-9]{30})/submit$', + survey_submit, + name='survey_submit' + ), url(r'^sync/(?P\d+|[A-Z]{8})/$',sync,name='sync_data'), url(r'^sync/$',sync,name='sync_data'), - url(r'^finished$', finished_view, name="finished_view") + url(r'^finished$', finished_view, name="finished_view"), + url(r'^worker/contact/(?P\d+)',contact_worker,name='contact_worker') ) diff --git a/expdj/apps/turk/utils.py b/expdj/apps/turk/utils.py index 6ec8592..1f9b803 100644 --- a/expdj/apps/turk/utils.py +++ b/expdj/apps/turk/utils.py @@ -1,16 +1,19 @@ -from expdj.apps.experiments.models import Experiment -from boto.mturk.connection import MTurkConnection -from expdj.settings import BASE_DIR, MTURK_ALLOW -from boto.mturk.question import ExternalQuestion -from boto.mturk.price import Price import ConfigParser import datetime -import pandas import json import os +from boto.mturk.connection import MTurkConnection +from boto.mturk.price import Price +from boto.mturk.question import ExternalQuestion +import pandas + from django.conf import settings +from expdj.apps.experiments.models import Experiment +from expdj.settings import BASE_DIR, MTURK_ALLOW + + # RESULTS UTILS def to_dict(input_ordered_dict): @@ -132,3 +135,6 @@ def get_time_difference(d1,d2,format='%Y-%m-%d %H:%M:%S'): if isinstance(d2,str): d2 = datetime.datetime.strptime(d2, format) return (d2 - d1).total_seconds() / 60 + + + diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 9c6aae0..76d3e3e 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -15,15 +15,14 @@ from django.utils import timezone from django.views.decorators.csrf import ensure_csrf_cookie -from expdj.apps.experiments.models import (Assignment, Battery, - ExperimentTemplate) +from expdj.apps.experiments.models import (Battery, ExperimentTemplate) from expdj.apps.experiments.views import (check_battery_edit_permission, check_mturk_access, get_battery_intro, deploy_battery) from expdj.apps.experiments.utils import get_experiment_type, select_experiments from expdj.apps.turk.forms import HITForm, WorkerContactForm from expdj.apps.turk.models import Worker, HIT, Assignment, Result, get_worker from expdj.apps.turk.tasks import (assign_experiment_credit, - check_battery_dependencies, get_unique_experiments) + get_unique_experiments) from expdj.apps.turk.utils import (get_connection, get_host, get_worker_url, get_worker_experiments) from expdj.settings import BASE_DIR,STATIC_ROOT,MEDIA_ROOT From e6230ea264ff2d21a38a4e2c503371352d6e0762 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 15:49:37 -0700 Subject: [PATCH 066/142] fixed a couple sytax issues --- expdj/apps/turk/forms.py | 2 +- expdj/apps/turk/views.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/expdj/apps/turk/forms.py b/expdj/apps/turk/forms.py index d5674a3..4f7ccf3 100644 --- a/expdj/apps/turk/forms.py +++ b/expdj/apps/turk/forms.py @@ -37,6 +37,6 @@ class WorkerContactForm(forms.Form): message = forms.CharField(label="Message") def __init__(self, *args, **kwargs): - super(ContactForm, self).__init__(*args, **kwargs) + super(WorkerContactForm, self).__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.add_input(Submit("submit", "Send")) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 76d3e3e..4f2f722 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -347,7 +347,7 @@ def contact_worker(request, aid): if mturk_permission == False: return HttpResponseForbidden() - assignment = Assignments.objects.get(aid) + assignment = Assignment.objects.get(id=aid) if request.method == "GET": form = WorkerContactForm() worker = assignment.worker @@ -355,7 +355,7 @@ def contact_worker(request, aid): "form": form, "worker": worker } - return(request, "contact_worker_modal.html", context) + return render(request, "turk/contact_worker_modal.html", context) elif request.method == "POST": form = WorkerContactForm(request.POST) if form.is_valid(): From ded399923b2d845a58a23128c89a075ca79be3bc Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 15:51:36 -0700 Subject: [PATCH 067/142] forgot to pass request to manage_hit when redirecting in contact worker --- expdj/apps/turk/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expdj/apps/turk/views.py b/expdj/apps/turk/views.py index 4f2f722..067014a 100644 --- a/expdj/apps/turk/views.py +++ b/expdj/apps/turk/views.py @@ -362,7 +362,8 @@ def contact_worker(request, aid): subject = form.cleaned_data['subject'] message = form.cleaned_data['message'] assignment.contact_worker(subject, message) - return manage_hit(assignment.hit.battery.id, assignment.hit.id) + return manage_hit(request, assignment.hit.battery.id, + assignment.hit.id) else: return HttpResponseNotAllowed() From e7ea322a0f34d64ef64084d27ce41943a0cb0cd7 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Sun, 3 Jul 2016 16:00:21 -0700 Subject: [PATCH 068/142] added missing form tags for contact worker modal --- expdj/apps/turk/templates/turk/contact_worker_modal.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expdj/apps/turk/templates/turk/contact_worker_modal.html b/expdj/apps/turk/templates/turk/contact_worker_modal.html index 8fdb15e..7d6f9ad 100644 --- a/expdj/apps/turk/templates/turk/contact_worker_modal.html +++ b/expdj/apps/turk/templates/turk/contact_worker_modal.html @@ -7,9 +7,9 @@