def enterexp(): """ AJAX listener that listens for a signal from the user's script when they leave the instructions and enter the real experiment. After the server receives this signal, it will no longer allow them to re-access the experiment applet (meaning they can't do part of the experiment and referesh to start over). """ app.logger.info("Accessing /inexp") if not 'uniqueId' in request.form: raise ExperimentError('improper_inputs') unique_id = request.form['uniqueId'] try: user = Participant.query.\ filter(Participant.uniqueid == unique_id).one() user.status = STARTED user.beginexp = datetime.datetime.now() db_session.add(user) db_session.commit() resp = {"status": "success"} except exc.SQLAlchemyError: app.logger.error("DB error: Unique user not found.") resp = {"status": "error, uniqueId not found"} return jsonify(**resp)
def quitter(): """ Mark quitter as such. """ unique_id = request.form['uniqueId'] if unique_id[:5] == "debug": debug_mode = True else: debug_mode = False if debug_mode: resp = {"status": "didn't mark as quitter since this is debugging"} return jsonify(**resp) else: try: unique_id = request.form['uniqueId'] app.logger.info("Marking quitter %s" % unique_id) user = Participant.query.\ filter(Participant.uniqueid == unique_id).\ one() user.status = QUITEARLY db_session.add(user) db_session.commit() except exc.SQLAlchemyError: raise ExperimentError('tried_to_quit') else: resp = {"status": "marked as quitter"} return jsonify(**resp)
def debug_complete(): ''' Debugging route for complete. ''' if not 'uniqueId' in request.args: raise ExperimentError('improper_inputs') else: unique_id = request.args['uniqueId'] try: user = Participant.query.\ filter(Participant.uniqueid == unique_id).one() user.status = COMPLETED user.endhit = datetime.datetime.now() db_session.add(user) db_session.commit() except: raise ExperimentError('error_setting_worker_complete') else: return render_template('complete.html')
def regularpage(foldername=None, pagename=None): """ Route not found by the other routes above. May point to a static template. """ if foldername is None and pagename is None: raise ExperimentError('page_not_found') if foldername is None and pagename is not None: return render_template(pagename) else: return render_template(foldername + "/" + pagename)
def give_consent(): """ Serves up the consent in the popup window. """ if not ('hitId' in request.args and 'assignmentId' in request.args and 'workerId' in request.args): raise ExperimentError( 'hit_assign_worker_id_not_set_in_consent') hitId = request.args['hitId'] assignmentId = request.args['assignmentId'] workerId = request.args['workerId'] return render_template('consent.html', hitid = hitId, assignmentid=assignmentId, workerid=workerId)
def get_ad_via_hitid(hitId): username = config.get('psiTurk Access', 'psiturk_access_key_id') password = config.get('psiTurk Access', 'psiturk_secret_access_id') try: r = requests.get('https://api.psiturk.org/api/ad/lookup/' + hitId, auth=(username,password)) except: raise ExperimentError('api_server_not_reachable') else: if r.status_code == 200: return r.json()['ad_id'] else: return "error"
def insert_mode(page_html, mode): ''' Insert mode ''' match_found = False matches = re.finditer('workerId={{ workerid }}', page_html) match = None for match in matches: match_found = True if match_found: new_html = page_html[:match.end()] + "&mode=" + mode +\ page_html[match.end():] return new_html else: raise ExperimentError("insert_mode_failed")
def give_consent(): """ Serves up the consent in the popup window. """ if not ('hitId' in request.args and 'assignmentId' in request.args and 'workerId' in request.args): raise ExperimentError('hit_assign_worker_id_not_set_in_consent') hit_id = request.args['hitId'] assignment_id = request.args['assignmentId'] worker_id = request.args['workerId'] mode = request.args['mode'] with open('templates/consent.html', 'r') as temp_file: consent_string = temp_file.read() consent_string = insert_mode(consent_string, mode) return render_template_string(consent_string, hitid=hit_id, assignmentid=assignment_id, workerid=worker_id)
def start_exp(): """ Serves up the experiment applet. """ if not ('hitId' in request.args and 'assignmentId' in request.args and 'workerId' in request.args): raise ExperimentError('hit_assign_worker_id_not_set_in_exp') hit_id = request.args['hitId'] assignment_id = request.args['assignmentId'] worker_id = request.args['workerId'] mode = request.args['mode'] app.logger.info("Accessing /exp: %(h)s %(a)s %(w)s " % { "h": hit_id, "a": assignment_id, "w": worker_id }) if hit_id[:5] == "debug": debug_mode = True else: debug_mode = False # Check first to see if this hitId or assignmentId exists. If so, check to # see if inExp is set matches = Participant.query.\ filter(Participant.workerid == worker_id).\ all() numrecs = len(matches) if numrecs == 0: # Choose condition and counterbalance subj_cond, subj_counter = get_random_condcount() worker_ip = "UNKNOWN" if not request.remote_addr else \ request.remote_addr browser = "UNKNOWN" if not request.user_agent.browser else \ request.user_agent.browser platform = "UNKNOWN" if not request.user_agent.platform else \ request.user_agent.platform language = "UNKNOWN" if not request.user_agent.language else \ request.user_agent.language # Set condition here and insert into database. participant_attributes = dict(assignmentid=assignment_id, workerid=worker_id, hitid=hit_id, cond=subj_cond, counterbalance=subj_counter, ipaddress=worker_ip, browser=browser, platform=platform, language=language) part = Participant(**participant_attributes) db_session.add(part) db_session.commit() else: # A couple possible problems here: # 1: They've already done an assignment, then we should tell them they # can't do another one # 2: They've already worked on this assignment, and got too far to # start over. # 3: They're in the database twice for the same assignment, that should # never happen. # 4: They're returning and all is well. nrecords = 0 for record in matches: other_assignment = False if record.assignmentid != assignment_id: other_assignment = True else: nrecords += 1 if nrecords <= 1 and not other_assignment: part = matches[0] # In experiment (or later) can't restart at this point if part.status >= STARTED and not debug_mode: raise ExperimentError('already_started_exp') else: if nrecords > 1: app.logger.error("Error, hit/assignment appears in database \ more than once (serious problem)") raise ExperimentError( 'hit_assign_appears_in_database_more_than_once') if other_assignment: raise ExperimentError('already_did_exp_hit') if mode == 'sandbox' or mode == 'live': # If everything goes ok here relatively safe to assume we can lookup # the ad. ad_id = get_ad_via_hitid(hit_id) if ad_id != "error": if mode == "sandbox": ad_server_location = 'https://sandbox.ad.psiturk.org/complete/'\ + str(ad_id) elif mode == "live": ad_server_location = 'https://ad.psiturk.org/complete/' +\ str(ad_id) else: raise ExperimentError('hit_not_registered_with_ad_server') else: ad_server_location = '/complete' return render_template('exp.html', uniqueId=part.uniqueid, condition=part.cond, counterbalance=part.counterbalance, adServerLoc=ad_server_location, mode=mode)
def advertisement(): """ This is the url we give for the ad for our 'external question'. The ad has to display two different things: This page will be called from within mechanical turk, with url arguments hitId, assignmentId, and workerId. If the worker has not yet accepted the hit: These arguments will have null values, we should just show an ad for the experiment. If the worker has accepted the hit: These arguments will have appropriate values and we should enter the person in the database and provide a link to the experiment popup. """ user_agent_string = request.user_agent.string user_agent_obj = user_agents.parse(user_agent_string) browser_ok = True for rule in string.split( CONFIG.get('HIT Configuration', 'browser_exclude_rule'), ','): myrule = rule.strip() if myrule in ["mobile", "tablet", "touchcapable", "pc", "bot"]: if (myrule == "mobile" and user_agent_obj.is_mobile) or\ (myrule == "tablet" and user_agent_obj.is_tablet) or\ (myrule == "touchcapable" and user_agent_obj.is_touch_capable) or\ (myrule == "pc" and user_agent_obj.is_pc) or\ (myrule == "bot" and user_agent_obj.is_bot): browser_ok = False elif myrule in user_agent_string: browser_ok = False if not browser_ok: # Handler for IE users if IE is not supported. raise ExperimentError('browser_type_not_allowed') if not ('hitId' in request.args and 'assignmentId' in request.args): raise ExperimentError('hit_assign_worker_id_not_set_in_mturk') hit_id = request.args['hitId'] assignment_id = request.args['assignmentId'] mode = request.args['mode'] if hit_id[:5] == "debug": debug_mode = True else: debug_mode = False already_in_db = False if 'workerId' in request.args: worker_id = request.args['workerId'] # First check if this workerId has completed the task before (v1). nrecords = Participant.query.\ filter(Participant.assignmentid != assignment_id).\ filter(Participant.workerid == worker_id).\ count() if nrecords > 0: # Already completed task already_in_db = True else: # If worker has not accepted the hit worker_id = None try: part = Participant.query.\ filter(Participant.hitid == hit_id).\ filter(Participant.assignmentid == assignment_id).\ filter(Participant.workerid == worker_id).\ one() status = part.status except exc.SQLAlchemyError: status = None if status == STARTED and not debug_mode: # Once participants have finished the instructions, we do not allow # them to start the task again. raise ExperimentError('already_started_exp_mturk') elif status == COMPLETED: # They've done the debriefing but perhaps haven't submitted the HIT # yet.. Turn asignmentId into original assignment id before sending it # back to AMT return render_template('thanks.html', is_sandbox=(mode == "sandbox"), hitid=hit_id, assignmentid=assignment_id, workerid=worker_id) elif already_in_db and not debug_mode: raise ExperimentError('already_did_exp_hit') elif status == ALLOCATED or not status or debug_mode: # Participant has not yet agreed to the consent. They might not # even have accepted the HIT. with open('templates/ad.html', 'r') as temp_file: ad_string = temp_file.read() ad_string = insert_mode(ad_string, mode) return render_template_string(ad_string, hitid=hit_id, assignmentid=assignment_id, workerid=worker_id) else: raise ExperimentError('status_incorrectly_set')
def advertisement(): """ This is the url we give for the ad for our 'external question'. The ad has to display two different things: This page will be called from within mechanical turk, with url arguments hitId, assignmentId, and workerId. If the worker has not yet accepted the hit: These arguments will have null values, we should just show an ad for the experiment. If the worker has accepted the hit: These arguments will have appropriate values and we should enter the person in the database and provide a link to the experiment popup. """ user_agent_string = request.user_agent.string user_agent_obj = user_agents.parse(user_agent_string) browser_ok = True for rule in string.split( CONFIG.get('HIT Configuration', 'browser_exclude_rule'), ','): myrule = rule.strip() if myrule in ["mobile", "tablet", "touchcapable", "pc", "bot"]: if (myrule == "mobile" and user_agent_obj.is_mobile) or\ (myrule == "tablet" and user_agent_obj.is_tablet) or\ (myrule == "touchcapable" and user_agent_obj.is_touch_capable) or\ (myrule == "pc" and user_agent_obj.is_pc) or\ (myrule == "bot" and user_agent_obj.is_bot): browser_ok = False elif (myrule == "Safari" or myrule == "safari"): if "Chrome" in user_agent_string and "Safari" in user_agent_string: pass elif "Safari" in user_agent_string: browser_ok = False elif myrule in user_agent_string: browser_ok = False if not browser_ok: # Handler for IE users if IE is not supported. raise ExperimentError('browser_type_not_allowed') if not ('hitId' in request.args and 'assignmentId' in request.args): raise ExperimentError('hit_assign_worker_id_not_set_in_mturk') hit_id = request.args['hitId'] assignment_id = request.args['assignmentId'] mode = request.args['mode'] if hit_id[:5] == "debug": debug_mode = True else: debug_mode = False already_in_db = False if 'workerId' in request.args: worker_id = request.args['workerId'] # First check if this workerId has completed the task before (v1). nrecords = Participant.query.\ filter(Participant.assignmentid != assignment_id).\ filter(Participant.workerid == worker_id).\ count() if nrecords > 0: # Already completed task already_in_db = True else: # If worker has not accepted the hit worker_id = None try: part = Participant.query.\ filter(Participant.hitid == hit_id).\ filter(Participant.assignmentid == assignment_id).\ filter(Participant.workerid == worker_id).\ one() status = part.status except exc.SQLAlchemyError: status = None allow_repeats = CONFIG.getboolean('HIT Configuration', 'allow_repeats') if (status == STARTED or status == QUITEARLY) and not debug_mode: # Once participants have finished the instructions, we do not allow # them to start the task again. raise ExperimentError('already_started_exp_mturk') elif status == COMPLETED or (status == SUBMITTED and not already_in_db): # 'or status == SUBMITTED' because we suspect that sometimes the post # to mturk fails after we've set status to SUBMITTED, so really they # have not successfully submitted. This gives another chance for the # submit to work when not using the psiturk ad server. use_psiturk_ad_server = CONFIG.getboolean('Shell Parameters', 'use_psiturk_ad_server') if not use_psiturk_ad_server: # They've finished the experiment but haven't successfully submitted the HIT # yet. return render_template( 'thanks-mturksubmit.html', using_sandbox=(mode == "sandbox"), hitid=hit_id, assignmentid=assignment_id, workerid=worker_id ) else: # Show them a thanks message and tell them to go away. return render_template( 'thanks.html' ) elif already_in_db and not (debug_mode or allow_repeats): raise ExperimentError('already_did_exp_hit') elif status == ALLOCATED or not status or debug_mode: # Participant has not yet agreed to the consent. They might not # even have accepted the HIT. with open('templates/ad.html', 'r') as temp_file: ad_string = temp_file.read() ad_string = insert_mode(ad_string, mode) return render_template_string( ad_string, hitid=hit_id, assignmentid=assignment_id, workerid=worker_id ) else: raise ExperimentError('status_incorrectly_set')