Skip to content

Commit

Permalink
version 1.0 (MTurk) (#42)
Browse files Browse the repository at this point in the history
* version 1.0

* update error page
  • Loading branch information
szorowi1 authored Aug 11, 2020
1 parent 3f96896 commit 7b1d3f0
Show file tree
Hide file tree
Showing 13 changed files with 430 additions and 213 deletions.
95 changes: 66 additions & 29 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from app import consent, alert, experiment, complete, error
from .io import write_metadata
from .utils import gen_code
__version__ = '0.9.8'
__version__ = '1.0'

## Define root directory.
ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
Expand All @@ -20,19 +20,19 @@
reject_dir = os.path.join(ROOT_DIR, cfg['IO']['REJECT'])
if not os.path.isdir(reject_dir): os.makedirs(reject_dir)

## Check Flask password.
if cfg['FLASK']['SECRET_KEY'] == "PLEASE_CHANGE_THIS":
msg = "WARNING: Flask password is currently default. This should be changed prior to production."
warnings.warn(msg)
## Check Flask mode; if debug mode, clear session variable.
debug = cfg['FLASK'].getboolean('DEBUG')
if debug:
warnings.warn("WARNING: Flask currently in debug mode. This should be changed prior to production.")

## Check Flask mode.
if cfg['FLASK']['DEBUG'] != "FALSE":
msg = "WARNING: Flask currently in debug mode. This should be changed prior to production."
warnings.warn(msg)
## Check Flask password.
secret_key = cfg['FLASK']['SECRET_KEY']
if secret_key == "PLEASE_CHANGE_THIS":
warnings.warn("WARNING: Flask password is currently default. This should be changed prior to production.")

## Initialize Flask application.
app = Flask(__name__)
app.secret_key = cfg['FLASK']['SECRET_KEY']
app.secret_key = secret_key

## Apply blueprints to the application.
app.register_blueprint(consent.bp)
Expand All @@ -45,11 +45,14 @@
@app.route('/')
def index():

## Debug mode: clear session.
if debug:
session.clear()

## Store directories in session object.
session['data'] = data_dir
session['metadata'] = meta_dir
session['reject'] = reject_dir
session['debug'] = cfg['FLASK']['DEBUG'] != "FALSE"

## Record incoming metadata.
info = dict(
Expand All @@ -63,6 +66,7 @@ def index():
tp_b = request.args.get('tp_b'), # TurkPrime metadata
c = request.args.get('c'), # TurkPrime metadata
tp_c = request.args.get('tp_c'), # TurkPrime metadata
address = request.remote_addr, # NivTurk metadata
browser = request.user_agent.browser, # User metadata
platform = request.user_agent.platform, # User metadata
version = request.user_agent.version, # User metadata
Expand All @@ -71,49 +75,82 @@ def index():
## Case 1: workerId absent.
if info['workerId'] is None:

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

## Case 2: mobile user.
elif info['platform'] in ['android','iphone','ipad','wii']:

## Redirect participant to error (admin error).
## Redirect participant to error (platform error).
return redirect(url_for('error.error', errornum=1001))

## Case 3: repeat visit, manually changed workerId.
## Case 3: repeat visit, preexisting log but no session data.
elif not 'workerId' in session and info['workerId'] in os.listdir(meta_dir):

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

## Case 3a: previously started experiment.
if 'experiment' in logs:

## Update metadata.
session['workerId'] = info['workerId']
session['ERROR'] = '1004: Suspected incognito user.'
session['complete'] = 'error'
write_metadata(session, ['ERROR','complete'], 'a')

## Redirect participant to error (previous participation).
return redirect(url_for('error.error', errornum=1004))

## Case 3b: no previous experiment starts.
else:

## Update metadata.
for k, v in info.items(): session[k] = v
session['WARNING'] = "Assigned new subId."
write_metadata(session, ['subId','WARNING'], 'a')

## Redirect participant to consent form.
return redirect(url_for('consent.consent'))

## Case 4: repeat visit, manually changed workerId.
elif 'workerId' in session and session['workerId'] != info['workerId']:

## Update metadata.
session['ERROR'] = '1002: workerId tampering detected.'
write_metadata(session, ['ERROR'], 'a')
session['ERROR'] = '1005: workerId tampering detected.'
session['complete'] = 'error'
write_metadata(session, ['ERROR','complete'], 'a')

## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1002))
return redirect(url_for('error.error', errornum=1005))

## Case 4: repeat visit, preexisting activity.
elif 'workerId' in session or info['workerId'] in os.listdir(meta_dir):
## Case 5: repeat visit, previously completed experiment.
elif 'complete' in session:

## Update metadata.
session['WARNING'] = "Revisited home."
write_metadata(session, ['WARNING'], 'a')

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

## Case 6: repeat visit, preexisting activity.
elif 'workerId' in session:

## Update metadata.
for k, v in info.items(): session[k] = v
session['WARNING'] = "Revisited home."
write_metadata(session, ['WARNING'], 'a')

## Redirect participant to consent form.
return redirect(url_for('consent.consent'))

## Case 5: first visit, workerId present.
## Case 7: first visit, workerId present.
else:

## Update metadata.
for k, v in info.items(): session[k] = v
write_metadata(session, ['workerId','hitId','assignmentId','subId','browser','platform','version'], 'w')
write_metadata(session, ['workerId','hitId','assignmentId','subId','address','browser','platform','version'], 'w')

## Redirect participant to consent form.
return redirect(url_for('consent.consent'))

## DEV NOTE:
## The following route is strictly for development purpose and should be commented out before deployment.
# @app.route('/clear')
# def clear():
# session.clear()
# return 'Complete!'
32 changes: 24 additions & 8 deletions app/alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,24 @@
def alert():
"""Present alert to participant."""

## Case 1: first visit.
if not 'alert' in session:
## Error-catching: screen for missing session.
if not 'workerId' in session:

## Update participant metadata.
session['alert'] = True
write_metadata(session, ['alert'], 'a')
## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))

## Present alert page.
return render_template('alert.html')
## Case 1: previously completed experiment.
elif 'complete' in session:

## Update metadata.
session['WARNING'] = "Revisited alert page."
write_metadata(session, ['WARNING'], 'a')

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

## Case 2: repeat visit.
else:
elif 'alert' in session:

## Update participant metadata.
session['WARNING'] = "Revisited alert page."
Expand All @@ -28,6 +34,16 @@ def alert():
## Redirect participant to error (previous participation).
return redirect(url_for('experiment.experiment'))

## Case 3: first visit.
else:

## Update participant metadata.
session['alert'] = True
write_metadata(session, ['alert'], 'a')

## Present alert page.
return render_template('alert.html')

@bp.route('/alert', methods=['POST'])
def alert_post():
"""Process participant repsonse to alert page."""
Expand Down
16 changes: 14 additions & 2 deletions app/app.ini
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
[FLASK]

# Flask secret key for encrypting session objects
# Suggested: get key from https://randomkeygen.com
SECRET_KEY = PLEASE_CHANGE_THIS
DEBUG = FALSE

# Toggle debug mode (allow repeat visits from same session)
# Accepts true or false
DEBUG = true

[IO]
DATA = ../data

# Path to metadata folder [default: ../metadata]
METADATA = ../metadata

# Path to data folder [default: ../data]
DATA = ../data

# Path to reject folder [default: ../reject]
REJECT = ../reject
55 changes: 43 additions & 12 deletions app/complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,52 @@
def complete():
"""Present completion screen to participant."""

## Error-catching: screen for previous visits.
if 'complete' in session:
## Access query string.
query_info = request.args

## Update participant metadata.
session['WARNING'] = "Revisited complete page."
## Confirm all TurkPrime metadata present.
fields = ['workerId','assignmentId','hitId','a','tp_a','b','tp_b','c','tp_c']
all_fields = all([f in query_info for f in fields])

## 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))

## Case 1: visit complete page with previous rejection.
elif session.get('complete') == 'reject':

## Update metadata.
session['WARNING'] = "Revisited complete."
write_metadata(session, ['WARNING'], 'a')

else:
## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1005))

## Case 2: visit complete page with previous error.
elif session.get('complete') == 'error':

## Update participant metadata.
session['complete'] = True
write_metadata(session, ['complete'], 'a')
## Update metadata.
session['WARNING'] = "Revisited complete."
write_metadata(session, ['WARNING'], 'a')

## DEV NOTE:
## If you want a custom completion code, replace the return statement with:
## > render_template('complete.html', value=session['complete'])
## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1005))

## Case 3: visit complete page but missing metadata.
elif session.get('complete') == 'success' and not all_fields:

## Update metadata.
session['WARNING'] = "Revisited complete."
write_metadata(session, ['WARNING'], 'a')

## Redirect participant with complete metadata.
url = "/complete?workerId=%s&assignmentId=%s&hitId=%s&a=%s&tp_a=%s&b=%s&tp_b=%s&c=%s&tp_c=%s" %(session['workerId'], session['assignmentId'], session['hitId'], session['a'], session['tp_a'], session['b'], session['tp_b'], session['c'], session['tp_c'])
return redirect(url)

## Case 4: all else.
else:

return render_template('complete.html')
## Redirect participant with completion code.
return render_template('complete.html')
51 changes: 39 additions & 12 deletions app/consent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,57 @@
def consent():
"""Present consent form to participant."""

## Case 1: first visit.
if not 'consent' in session:
## 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))

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

## Update metadata.
session['WARNING'] = "Revisited consent page."
write_metadata(session, ['WARNING'], 'a')

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

## Case 2: first visit.
elif not 'consent' in session:

## Present consent form.
return render_template('consent.html')

## Case 2: repeat visit, previous consent.
elif session['consent']:
## Case 3: repeat visit, previous bot-detection.
elif session['consent'] == 'BOT':

## Update participant metadata.
session['WARNING'] = "Revisited consent form."
write_metadata(session, ['WARNING'], 'a')

## Redirect participant to alert page.
return redirect(url_for('alert.alert'))
## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1005))

## Case 3: repeat visit, previous non-consent.
else:
## Case 4: repeat visit, previous non-consent.
elif session['consent'] == False:

## Update participant metadata.
session['WARNING'] = "Revisited consent form."
write_metadata(session, ['WARNING'], 'a')

## Redirect participant to error (decline consent).
return redirect(url_for('error.error', errornum=1003))
return redirect(url_for('error.error', errornum=1002))

## Case 5: repeat visit, previous consent.
else:

## Update participant metadata.
session['WARNING'] = "Revisited consent form."
write_metadata(session, ['WARNING'], 'a')

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

@bp.route('/consent', methods=['POST'])
def consent_post():
Expand All @@ -48,10 +73,12 @@ def consent_post():

## Update participant metadata.
session['consent'] = 'BOT'
write_metadata(session, ['consent'], 'a')
session['experiment'] = False # Prevents incognito users
session['complete'] = 'error'
write_metadata(session, ['consent','experiment','complete'], 'a')

## Redirect participant to error (unusual activity).
return redirect(url_for('error.error', errornum=1002))
return redirect(url_for('error.error', errornum=1005))

## Check participant response.
elif subj_consent:
Expand All @@ -70,4 +97,4 @@ def consent_post():
write_metadata(session, ['consent'], 'a')

## Redirect participant to error (decline consent).
return redirect(url_for('error.error', errornum=1003))
return redirect(url_for('error.error', errornum=1002))
Loading

0 comments on commit 7b1d3f0

Please sign in to comment.