Experiment.py

Created Diff never expires
59 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
158 lines
297 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
392 lines
from flask import (Blueprint, redirect, render_template, request, session, url_for)
from flask import (Blueprint, redirect, render_template, request, session, url_for)
from .io import write_data, write_metadata
from .db import add_data, get_worker
from .io import write_data, write_metadata, write_data_interval
from datetime import datetime, timedelta
import os, re


## Initialize blueprint.
## Initialize blueprint.
bp = Blueprint('experiment', __name__)
bp = Blueprint('experiment', __name__)


@bp.route('/experiment')
@bp.route('/main')
def experiment():
def main():
"""Present jsPsych experiment to participant."""
"""Present game select screen to participant."""


## Error-catching: screen for missing session.
## Error-catching: screen for missing session.
if not 'workerId' in session:
if not 'workerId' in session:


## Redirect participant to error (missing workerId).
## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))
return redirect(url_for('error.error', errornum=1000))


## Case 1: previously completed experiment.
## Previous completion.
elif 'complete' in session:
elif 'complete' in session:


## Redirect participant to complete page.
## Redirect participant to complete page.
return redirect(url_for('complete.complete'))
return redirect(url_for('complete.complete'))


## Case 2: repeat visit.
## Query database for record of experiment completions.
elif not session['allow_restart'] and 'experiment' in session:
stage = get_worker(session['db_path'], session['workerId'])

## Case 1: context survey incomplete.
if not stage: # or (('page' not in session) and (stage !=5 )): # no completion or previously completed battery

## Present game select page.
return redirect(url_for('experiment.context'))

## Case 2: context survey complete.
else:

##stage = (stage - 1) % 4
## Present game select page.
return render_template('main.html', stage=(stage - 1), workerId=session['workerId'], assignmentId=session['assignmentId'], hitId=session['hitId'], code_success=session['code_success'], code_reject=session['code_reject'])

@bp.route('/experiments/context')
def context():
"""Present context survey to participant."""

## Error-catching: screen for missing session.
if not 'workerId' in session:

## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))

else:


## Update participant metadata.
## Update participant metadata.
session['ERROR'] = "1004: Revisited experiment."
session['context'] = 'start'
session['complete'] = 'error'
write_metadata(session, ['context'], 'a')
write_metadata(session, ['ERROR','complete'], 'a')


## Redirect participant to error (previous participation).
## Present experiment.
return redirect(url_for('error.error', errornum=1004))
return render_template('experiments/context.html', workerId=session['workerId'], assignmentId=session['assignmentId'], hitId=session['hitId'], code_success=session['code_success'], code_reject=session['code_reject'])


## Case 3: first visit.
@bp.route('/experiments/acqext', methods = ['GET', 'POST'])
def acqext():
#Allow writing data to file during experiment
if request.method == 'POST':
JSON = request.get_json()
## Save jsPsych data to disk.
write_data_interval(session, 'acqext', JSON)
return ('', 200)

"""Present acquistion & extinction task to participant."""

## Error-catching: screen for missing session.
if not 'workerId' in session:

## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))

else:
else:
## Parse log file.
with open(os.path.join(session['metadata'], session['workerId']), 'r') as f:
logs = f.read()

#Check for last successful completion
reaccess_gap = timedelta(days = 1000000)
list_success = re.findall('(.*)\tacqext\tsuccess\n', logs)

if len(list_success) > 0:
success_time = datetime.strptime(list_success[-1], '%Y-%m-%d %H:%M:%S')


#if time since last successful completion is too short
if (datetime.now() - success_time) < reaccess_gap:
session['acqext'] = 'attempted reaccess'
write_metadata(session, ['acqext'], 'a')
## Redirect participant to error.
return redirect(url_for('error.error', errornum=1007))
## Update participant metadata.
## Update participant metadata.
session['experiment'] = True
session['acqext'] = 'start'
write_metadata(session, ['experiment'], 'a')
write_metadata(session, ['acqext'], 'a')


## Present experiment.
## Present experiment.
return render_template('experiment.html', workerId=session['workerId'], assignmentId=session['assignmentId'], hitId=session['hitId'], code_success=session['code_success'], code_reject=session['code_reject'])
return render_template('experiments/acqext.html', workerId=session['workerId'], assignmentId=session['assignmentId'], hitId=session['hitId'], code_success=session['code_success'], code_reject=session['code_reject'])

# @bp.route('/experiments/risk', methods = ['GET', 'POST'])
# def risk():
# #Allow writing data to file during experiment
# if request.method == 'POST':
# JSON = request.get_json()
# ## Save jsPsych data to disk.
# write_data_interval(session, 'risk', JSON)
# return ('', 200)

# """Present risk sensitivity task to participant."""

# ## Error-catching: screen for missing session.
# if not 'workerId' in session:

# ## Redirect participant to error (missing workerId).
# return redirect(url_for('error.error', errornum=1000))

# else:
# ## Parse log file.
# with open(os.path.join(session['metadata'], session['workerId']), 'r') as f:
# logs = f.read()

# #Check for last successful completion
# reaccess_gap = timedelta(days = 1000000)
# list_success = re.findall('(.*)\trisk\tsuccess\n', logs)

# if len(list_success) > 0:
# success_time = datetime.strptime(list_success[-1], '%Y-%m-%d %H:%M:%S')

# #if time since last successful completion is too short
# if (datetime.now() - success_time) < reaccess_gap:
# session['risk'] = 'attempted reaccess'
# write_metadata(session, ['risk'], 'a')
# ## Redirect participant to error.
# return redirect(url_for('error.error', errornum=1007))
# ## Update participant metadata.
# session['risk'] = 'start'
# write_metadata(session, ['risk'], 'a')

# ## Present experiment.
# return render_template('experiments/risk.html', workerId=session['workerId'], assignmentId=session['assignmentId'], hitId=session['hitId'], code_success=session['code_success'], code_reject=session['code_reject'])


@bp.route('/experiments/stop', methods = ['GET', 'POST'])
def stop():
#Allow writing data to file during experiment
if request.method == 'POST':
JSON = request.get_json()
## Save jsPsych data to disk.
write_data_interval(session, 'stop', JSON)
return ('', 200)

"""Present stop signal task to participant."""

## Error-catching: screen for missing session.
if not 'workerId' in session:

## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))

else:
## Parse log file.
with open(os.path.join(session['metadata'], session['workerId']), 'r') as f:
logs = f.read()

#Check for last successful completion
reaccess_gap = timedelta(days = 1000000)
list_success = re.findall('(.*)\stop\tsuccess\n', logs)

if len(list_success) > 0:
success_time = datetime.strptime(list_success[-1], '%Y-%m-%d %H:%M:%S')

#if time since last successful completion is too short
if (datetime.now() - success_time) < reaccess_gap:
session['stop'] = 'attempted reaccess'
write_metadata(session, ['stop'], 'a')
## Redirect participant to error.
return redirect(url_for('error.error', errornum=1007))
## Update participant metadata.
session['stop'] = 'start'
write_metadata(session, ['stop'], 'a')

## Present experiment.
return render_template('experiments/stop.html', workerId=session['workerId'], assignmentId=session['assignmentId'], hitId=session['hitId'], code_success=session['code_success'], code_reject=session['code_reject'])



@bp.route('/experiments/recovery', methods = ['GET', 'POST'])
def recovery():
#Allow writing data to file during experiment
if request.method == 'POST':
JSON = request.get_json()
## Save jsPsych data to disk.
write_data_interval(session, 'recovery', JSON)
return ('', 200)

"""Present recovery task to participant."""

## Error-catching: screen for missing session.
if not 'workerId' in session:

## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))

else:
## Parse log file.
with open(os.path.join(session['metadata'], session['workerId']), 'r') as f:
logs = f.read()

#Check for last successful completion
reaccess_gap = timedelta(days = 1000000)
list_success = re.findall('(.*)\trecovery\tsuccess\n', logs)
# # Check for last completion of acquisition/extinction
# list_success_acqext = re.findall('(.*)\tacqext\tsuccess\n', logs)

# if len(list_success_acqext) > 0:
# success_time_acqext = datetime.strptime(list_success_acqext[-1], '%Y-%m-%d %H:%M:%S')

# # Check if 12 hours have passed since acqext completion
# if (datetime.now() - success_time_acqext) < timedelta(hours=12):
# session['recovery'] = 'attempted reaccess'
# write_metadata(session, ['recovery'], 'a')
# ## Redirect participant to error.
# return redirect(url_for('error.error', errornum=1010))

if len(list_success) > 0:
success_time = datetime.strptime(list_success[-1], '%Y-%m-%d %H:%M:%S')

#if time since last successful completion is too short
if (datetime.now() - success_time) < reaccess_gap:
session['recovery'] = 'attempted reaccess'
write_metadata(session, ['recovery'], 'a')
## Redirect participant to error.
return redirect(url_for('error.error', errornum=1007))
## Update participant metadata.
session['recovery'] = 'start'
write_metadata(session, ['recovery'], 'a')

## Present experiment.
return render_template('experiments/recovery.html', workerId=session['workerId'], assignmentId=session['assignmentId'], hitId=session['hitId'], code_success=session['code_success'], code_reject=session['code_reject'])



@bp.route('/experiment', methods=['POST'])
@bp.route('/experiment', methods=['POST'])
def pass_message():
def pass_message():
"""Write jsPsych message to metadata."""
"""Write jsPsych message to metadata."""


## Retrieve experiment.
page = request.args.get('experiment')

if request.is_json:
if request.is_json:


## Retrieve jsPsych data.
## Retrieve jsPsych data.
msg = request.get_json()
msg = request.get_json()


## Update participant metadata.
## Update participant metadata.
session['MESSAGE'] = msg
session[page] = msg
write_metadata(session, ['MESSAGE'], 'a')
write_metadata(session, [page], 'a')


## DEV NOTE:
## DEV NOTE:
## This function returns the HTTP response status code: 200
## This function returns the HTTP response status code: 200
## Code 200 signifies the POST request has succeeded.
## Code 200 signifies the POST request has succeeded.
## For a full list of status codes, see:
## For a full list of status codes, see:
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
return ('', 200)
return ('', 200)


@bp.route('/incomplete_save', methods=['POST'])
# commented out for KALEY TEST
def incomplete_save():
# @bp.route('/on_success', methods = ['POST'])
"""Save incomplete jsPsych dataset to disk."""
# def on_success():
# """Save complete jsPsych dataset to disk & redirect back to Home page."""

# ## Retrieve experiment.
# page = request.args.get('experiment')
# print(f"/on_success backend page: {page}")

# if request.is_json:
# print(f"/on_success is_json: TRUE")

# ## Retrieve jsPsych data.
# JSON = request.get_json()

# ## Save jsPsych data to disk.
# session['page'] = page
# write_data(session, JSON, method='success')
# print(f'write_data success')

# ## Save jsPsych data to database.
# add_data(session['db_path'], (session['workerId'], session['subId'], session['page'], JSON))
# print(f'add_data success')
# ## Update participant metadata.
# session[page] = 'success'
# write_metadata(session, [page], 'a')
# print(f'add_metadata success')

# ## DEV NOTE:
# ## This function returns the HTTP response status code: 200
# ## Code 200 signifies the POST request has succeeded.
# ## The corresponding jsPsych function handles the redirect.
# ## For a full list of status codes, see:
# ## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
# return ('', 200)


# KALEY TESTING:
@bp.route("/on_success", methods=["POST"])
def on_success():
if request.is_json:
if request.is_json:

payload = request.get_json()
## Retrieve jsPsych data.
# print('received payload')
JSON = request.get_json()
experiment = payload.get("experiment")
print(f'experiment: {experiment}')
jspsych_data = payload.get("data")
# print(f'data: {jspsych_data}')
# print(f"Received experiment: {experiment}")
# print(f"First 100 chars of data: {str(jspsych_data)[:100]}")


## Save jsPsch data to disk.
session['page'] = experiment
write_data(session, JSON, method='incomplete')
write_data(session, jspsych_data, method='success')
add_data(session['db_path'], (session['workerId'], session['subId'], experiment, jspsych_data))
session[experiment] = 'success'
write_metadata(session, [experiment], 'a')
return ('', 200)
return ('Expected JSON data', 400)


## Flag partial data saving.
session['MESSAGE'] = 'incomplete dataset saved'
write_metadata(session, ['MESSAGE'], 'a')


## DEV NOTE:
## This function returns the HTTP response status code: 200
## Code 200 signifies the POST request has succeeded.
## For a full list of status codes, see:
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
return ('', 200)


@bp.route('/redirect_success', methods = ['POST'])
@bp.route('/redirect_success')
def redirect_success():
def redirect_success():
"""Save complete jsPsych dataset to disk."""
"""Save complete jsPsych dataset to disk."""


if request.is_json:

## Retrieve jsPsych data.
JSON = request.get_json()

## Save jsPsch data to disk.
write_data(session, JSON, method='pass')

## Flag experiment as complete.
## Flag experiment as complete.
session['complete'] = 'success'
session['complete'] = 'success'
write_metadata(session, ['complete','code_success'], 'a')
write_metadata(session, ['complete','code_success'], 'a')


## DEV NOTE:
return redirect(url_for('complete.complete'))
## This function returns the HTTP response status code: 200
## Code 200 signifies the POST request has succeeded.
## The corresponding jsPsych function handles the redirect.
## For a full list of status codes, see:
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
return ('', 200)


@bp.route('/redirect_reject', methods = ['POST'])
@bp.route('/redirect_reject', methods = ['POST'])
def redirect_reject():
def redirect_reject():
"""Save rejected jsPsych dataset to disk."""
"""Save rejected jsPsych dataset to disk."""


## Retrieve experiment.
session['page'] = request.args.get('experiment')

if request.is_json:
if request.is_json:


## Retrieve jsPsych data.
## Retrieve jsPsych data.
JSON = request.get_json()
JSON = request.get_json()


## Save jsPsch data to disk.
## Save jsPsych data to disk.
write_data(session, JSON, method='reject')
write_data(session, JSON, method='reject')


## Flag experiment as complete.
## Flag experiment as complete.
session['complete'] = 'reject'
session['complete'] = 'reject'
write_metadata(session, ['complete','code_reject'], 'a')
write_metadata(session, ['complete'], 'a')


## DEV NOTE:
## DEV NOTE:
## This function returns the HTTP response status code: 200
## This function returns the HTTP response status code: 200
## Code 200 signifies the POST request has succeeded.
## Code 200 signifies the POST request has succeeded.
## The corresponding jsPsych function handles the redirect.
## The corresponding jsPsych function handles the redirect.
## For a full list of status codes, see:
## For a full list of status codes, see:
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
return ('', 200)
return ('', 200)


@bp.route('/redirect_error', methods = ['POST'])
@bp.route('/redirect_error', methods = ['POST'])
def redirect_error():
def redirect_error():
"""Save rejected jsPsych dataset to disk."""
"""Save rejected jsPsych dataset to disk."""


if request.is_json:
if request.is_json:


## Retrieve jsPsych data.
## Retrieve jsPsych data.
JSON = request.get_json()
JSON = request.get_json()


## Save jsPsch data to disk.
## Save jsPsch data to disk.
write_data(session, JSON, method='reject')
write_data(session, JSON, method='reject')


## Flag experiment as complete.
## Flag experiment as complete.
session['complete'] = 'error'
session['complete'] = 'error'
write_metadata(session, ['complete'], 'a')
write_metadata(session, ['complete'], 'a')


## DEV NOTE:
## DEV NOTE:
## This function returns the HTTP response status code: 200
## This function returns the HTTP response status code: 200
## Code 200 signifies the POST request has succeeded.
## Code 200 signifies the POST request has succeeded.
## The corresponding jsPsych function handles the redirect.
## The corresponding jsPsych function handles the redirect.
## For a full list of status codes, see:
## For a full list of status codes, see:
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
return ('', 200)
return ('', 200)