Skip to content

Commit 7b1d3f0

Browse files
authored
version 1.0 (MTurk) (#42)
* version 1.0 * update error page
1 parent 3f96896 commit 7b1d3f0

File tree

13 files changed

+430
-213
lines changed

13 files changed

+430
-213
lines changed

app/__init__.py

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from app import consent, alert, experiment, complete, error
44
from .io import write_metadata
55
from .utils import gen_code
6-
__version__ = '0.9.8'
6+
__version__ = '1.0'
77

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

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

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

3333
## Initialize Flask application.
3434
app = Flask(__name__)
35-
app.secret_key = cfg['FLASK']['SECRET_KEY']
35+
app.secret_key = secret_key
3636

3737
## Apply blueprints to the application.
3838
app.register_blueprint(consent.bp)
@@ -45,11 +45,14 @@
4545
@app.route('/')
4646
def index():
4747

48+
## Debug mode: clear session.
49+
if debug:
50+
session.clear()
51+
4852
## Store directories in session object.
4953
session['data'] = data_dir
5054
session['metadata'] = meta_dir
5155
session['reject'] = reject_dir
52-
session['debug'] = cfg['FLASK']['DEBUG'] != "FALSE"
5356

5457
## Record incoming metadata.
5558
info = dict(
@@ -63,6 +66,7 @@ def index():
6366
tp_b = request.args.get('tp_b'), # TurkPrime metadata
6467
c = request.args.get('c'), # TurkPrime metadata
6568
tp_c = request.args.get('tp_c'), # TurkPrime metadata
69+
address = request.remote_addr, # NivTurk metadata
6670
browser = request.user_agent.browser, # User metadata
6771
platform = request.user_agent.platform, # User metadata
6872
version = request.user_agent.version, # User metadata
@@ -71,49 +75,82 @@ def index():
7175
## Case 1: workerId absent.
7276
if info['workerId'] is None:
7377

74-
## Redirect participant to error (admin error).
78+
## Redirect participant to error (missing workerId).
7579
return redirect(url_for('error.error', errornum=1000))
7680

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

80-
## Redirect participant to error (admin error).
84+
## Redirect participant to error (platform error).
8185
return redirect(url_for('error.error', errornum=1001))
8286

83-
## Case 3: repeat visit, manually changed workerId.
87+
## Case 3: repeat visit, preexisting log but no session data.
88+
elif not 'workerId' in session and info['workerId'] in os.listdir(meta_dir):
89+
90+
## Consult log file.
91+
with open(os.path.join(session['metadata'], info['workerId']),'r') as f:
92+
logs = f.read()
93+
94+
## Case 3a: previously started experiment.
95+
if 'experiment' in logs:
96+
97+
## Update metadata.
98+
session['workerId'] = info['workerId']
99+
session['ERROR'] = '1004: Suspected incognito user.'
100+
session['complete'] = 'error'
101+
write_metadata(session, ['ERROR','complete'], 'a')
102+
103+
## Redirect participant to error (previous participation).
104+
return redirect(url_for('error.error', errornum=1004))
105+
106+
## Case 3b: no previous experiment starts.
107+
else:
108+
109+
## Update metadata.
110+
for k, v in info.items(): session[k] = v
111+
session['WARNING'] = "Assigned new subId."
112+
write_metadata(session, ['subId','WARNING'], 'a')
113+
114+
## Redirect participant to consent form.
115+
return redirect(url_for('consent.consent'))
116+
117+
## Case 4: repeat visit, manually changed workerId.
84118
elif 'workerId' in session and session['workerId'] != info['workerId']:
85119

86120
## Update metadata.
87-
session['ERROR'] = '1002: workerId tampering detected.'
88-
write_metadata(session, ['ERROR'], 'a')
121+
session['ERROR'] = '1005: workerId tampering detected.'
122+
session['complete'] = 'error'
123+
write_metadata(session, ['ERROR','complete'], 'a')
89124

90125
## Redirect participant to error (unusual activity).
91-
return redirect(url_for('error.error', errornum=1002))
126+
return redirect(url_for('error.error', errornum=1005))
92127

93-
## Case 4: repeat visit, preexisting activity.
94-
elif 'workerId' in session or info['workerId'] in os.listdir(meta_dir):
128+
## Case 5: repeat visit, previously completed experiment.
129+
elif 'complete' in session:
130+
131+
## Update metadata.
132+
session['WARNING'] = "Revisited home."
133+
write_metadata(session, ['WARNING'], 'a')
134+
135+
## Redirect participant to complete page.
136+
return redirect(url_for('complete.complete'))
137+
138+
## Case 6: repeat visit, preexisting activity.
139+
elif 'workerId' in session:
95140

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

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

104-
## Case 5: first visit, workerId present.
148+
## Case 7: first visit, workerId present.
105149
else:
106150

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

111155
## Redirect participant to consent form.
112156
return redirect(url_for('consent.consent'))
113-
114-
## DEV NOTE:
115-
## The following route is strictly for development purpose and should be commented out before deployment.
116-
# @app.route('/clear')
117-
# def clear():
118-
# session.clear()
119-
# return 'Complete!'

app/alert.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,24 @@
88
def alert():
99
"""Present alert to participant."""
1010

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

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

18-
## Present alert page.
19-
return render_template('alert.html')
17+
## Case 1: previously completed experiment.
18+
elif 'complete' in session:
19+
20+
## Update metadata.
21+
session['WARNING'] = "Revisited alert page."
22+
write_metadata(session, ['WARNING'], 'a')
23+
24+
## Redirect participant to complete page.
25+
return redirect(url_for('complete.complete'))
2026

2127
## Case 2: repeat visit.
22-
else:
28+
elif 'alert' in session:
2329

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

37+
## Case 3: first visit.
38+
else:
39+
40+
## Update participant metadata.
41+
session['alert'] = True
42+
write_metadata(session, ['alert'], 'a')
43+
44+
## Present alert page.
45+
return render_template('alert.html')
46+
3147
@bp.route('/alert', methods=['POST'])
3248
def alert_post():
3349
"""Process participant repsonse to alert page."""

app/app.ini

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
[FLASK]
2+
3+
# Flask secret key for encrypting session objects
4+
# Suggested: get key from https://randomkeygen.com
25
SECRET_KEY = PLEASE_CHANGE_THIS
3-
DEBUG = FALSE
6+
7+
# Toggle debug mode (allow repeat visits from same session)
8+
# Accepts true or false
9+
DEBUG = true
410

511
[IO]
6-
DATA = ../data
12+
13+
# Path to metadata folder [default: ../metadata]
714
METADATA = ../metadata
15+
16+
# Path to data folder [default: ../data]
17+
DATA = ../data
18+
19+
# Path to reject folder [default: ../reject]
820
REJECT = ../reject

app/complete.py

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,52 @@
88
def complete():
99
"""Present completion screen to participant."""
1010

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

14-
## Update participant metadata.
15-
session['WARNING'] = "Revisited complete page."
14+
## Confirm all TurkPrime metadata present.
15+
fields = ['workerId','assignmentId','hitId','a','tp_a','b','tp_b','c','tp_c']
16+
all_fields = all([f in query_info for f in fields])
17+
18+
## Error-catching: screen for missing session.
19+
if not 'workerId' in session:
20+
21+
## Redirect participant to error (missing workerId).
22+
return redirect(url_for('error.error', errornum=1000))
23+
24+
## Case 1: visit complete page with previous rejection.
25+
elif session.get('complete') == 'reject':
26+
27+
## Update metadata.
28+
session['WARNING'] = "Revisited complete."
1629
write_metadata(session, ['WARNING'], 'a')
1730

18-
else:
31+
## Redirect participant to error (unusual activity).
32+
return redirect(url_for('error.error', errornum=1005))
33+
34+
## Case 2: visit complete page with previous error.
35+
elif session.get('complete') == 'error':
1936

20-
## Update participant metadata.
21-
session['complete'] = True
22-
write_metadata(session, ['complete'], 'a')
37+
## Update metadata.
38+
session['WARNING'] = "Revisited complete."
39+
write_metadata(session, ['WARNING'], 'a')
2340

24-
## DEV NOTE:
25-
## If you want a custom completion code, replace the return statement with:
26-
## > render_template('complete.html', value=session['complete'])
41+
## Redirect participant to error (unusual activity).
42+
return redirect(url_for('error.error', errornum=1005))
43+
44+
## Case 3: visit complete page but missing metadata.
45+
elif session.get('complete') == 'success' and not all_fields:
46+
47+
## Update metadata.
48+
session['WARNING'] = "Revisited complete."
49+
write_metadata(session, ['WARNING'], 'a')
50+
51+
## Redirect participant with complete metadata.
52+
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'])
53+
return redirect(url)
54+
55+
## Case 4: all else.
56+
else:
2757

28-
return render_template('complete.html')
58+
## Redirect participant with completion code.
59+
return render_template('complete.html')

app/consent.py

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,57 @@
88
def consent():
99
"""Present consent form to participant."""
1010

11-
## Case 1: first visit.
12-
if not 'consent' in session:
11+
## Error-catching: screen for missing session.
12+
if not 'workerId' in session:
13+
14+
## Redirect participant to error (missing workerId).
15+
return redirect(url_for('error.error', errornum=1000))
16+
17+
## Case 1: previously completed experiment.
18+
elif 'complete' in session:
19+
20+
## Update metadata.
21+
session['WARNING'] = "Revisited consent page."
22+
write_metadata(session, ['WARNING'], 'a')
23+
24+
## Redirect participant to complete page.
25+
return redirect(url_for('complete.complete'))
26+
27+
## Case 2: first visit.
28+
elif not 'consent' in session:
1329

1430
## Present consent form.
1531
return render_template('consent.html')
1632

17-
## Case 2: repeat visit, previous consent.
18-
elif session['consent']:
33+
## Case 3: repeat visit, previous bot-detection.
34+
elif session['consent'] == 'BOT':
1935

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

24-
## Redirect participant to alert page.
25-
return redirect(url_for('alert.alert'))
40+
## Redirect participant to error (unusual activity).
41+
return redirect(url_for('error.error', errornum=1005))
2642

27-
## Case 3: repeat visit, previous non-consent.
28-
else:
43+
## Case 4: repeat visit, previous non-consent.
44+
elif session['consent'] == False:
2945

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

3450
## Redirect participant to error (decline consent).
35-
return redirect(url_for('error.error', errornum=1003))
51+
return redirect(url_for('error.error', errornum=1002))
3652

53+
## Case 5: repeat visit, previous consent.
54+
else:
55+
56+
## Update participant metadata.
57+
session['WARNING'] = "Revisited consent form."
58+
write_metadata(session, ['WARNING'], 'a')
59+
60+
## Redirect participant to alert page.
61+
return redirect(url_for('alert.alert'))
3762

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

4974
## Update participant metadata.
5075
session['consent'] = 'BOT'
51-
write_metadata(session, ['consent'], 'a')
76+
session['experiment'] = False # Prevents incognito users
77+
session['complete'] = 'error'
78+
write_metadata(session, ['consent','experiment','complete'], 'a')
5279

5380
## Redirect participant to error (unusual activity).
54-
return redirect(url_for('error.error', errornum=1002))
81+
return redirect(url_for('error.error', errornum=1005))
5582

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

7299
## Redirect participant to error (decline consent).
73-
return redirect(url_for('error.error', errornum=1003))
100+
return redirect(url_for('error.error', errornum=1002))

0 commit comments

Comments
 (0)