Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Light editing #3

Open
wants to merge 19 commits into
base: prolific
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ COPY requirements.txt .
RUN pip install -r requirements.txt &&\
playwright install && \
playwright install-deps
RUN apt-get install -y cmake genisoimage && \
cd / && \
git clone https://github.com/planetbeing/libdmg-hfsplus.git && \
cd libdmg-hfsplus && \
git fetch && \
git checkout openssl-1.1 && \
cmake . && \
make && \
mv dmg/dmg /usr/local/bin/ && \
cd /
RUN curl https://sh.rustup.rs -sSf > install_rust.sh && \
bash install_rust.sh -y && \
. "$HOME/.cargo/env" && \
cargo install --git https://github.com/indygreg/apple-platform-rs --branch main --bin rcodesign apple-codesign && \
cd ../cogmood_backend

COPY . .
ENV SCRIPT_NAME="/cogmood"
CMD ["gunicorn", "-b", "0.0.0.0:8000", "-w", "10", "-k", "gevent", "--max-requests", "5000", "app:app"]
4 changes: 4 additions & 0 deletions app/app.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ ALLOW_RESTART = true
# Accepts true or false
DEBUG = true

# Toggle route debug mode (way more logging from routing.py)
# Accepts true or false
ROUTE_DEBUG = false

# allowed user agents [default: ["macintosh", "windows"]]
ALLOWED_AGENTS = ["macintosh", "windows"]

Expand Down
8 changes: 8 additions & 0 deletions app/complete.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from flask import (Blueprint, redirect, render_template, request, session, url_for)
from .io import write_metadata
from .config import CFG
from .utils import logging

## Initialize blueprint.
bp = Blueprint('complete', __name__)
Expand All @@ -9,6 +10,13 @@
def complete():
"""Present completion screen to participant."""

route_debug = CFG['route_debug']
if route_debug:
logging.info(f'Request to complete')
logging.info('Session contents')
for k, v in session.items():
logging.info(f"{k}: {v}")

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

Expand Down
8 changes: 5 additions & 3 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
if not os.path.isdir(task_badupload_dir): os.makedirs(task_badupload_dir)
dl_dir = os.path.join(data_dir, 'download')
if not os.path.isdir(dl_dir): os.makedirs(dl_dir)
exe_dir = os.path.join(data_dir, 'exe')
if not os.path.isdir(exe_dir): os.makedirs(exe_dir)
base_exe_dir = os.path.join(data_dir, 'base_executables')
if not os.path.isdir(base_exe_dir): os.makedirs(base_exe_dir)
survey_dir = os.path.join(data_dir, 'survey')
if not os.path.isdir(survey_dir): os.makedirs(survey_dir)
survey_incomplete_dir = os.path.join(survey_dir, 'incomplete')
Expand All @@ -44,6 +44,7 @@

CFG = dict(
debug=cfg['FLASK'].getboolean('DEBUG'),
route_debug=cfg['FLASK'].getboolean('ROUTE_DEBUG'),
allow_restart=cfg['FLASK'].getboolean('ALLOW_RESTART'),
code_success=cfg['PROLIFIC']['CODE_SUCCESS'],
code_reject=cfg['PROLIFIC']['CODE_REJECT'],
Expand All @@ -65,7 +66,8 @@
s_reject=survey_reject_dir,
s_complete=survey_complete_dir,
download=dl_dir,
exe=dl_dir,
base_exe=os.path.join(base_exe_dir, 'SUPREME.exe'),
base_app=os.path.join(base_exe_dir, 'SUPREME.app'),
disallowed_agents=json.loads(cfg['FLASK']['DISALLOWED_AGENTS']),
allowed_agents=json.loads(cfg['FLASK']['ALLOWED_AGENTS']),
blocks=json.loads(cfg['SUPREME']['BLOCKS']),
Expand Down
42 changes: 34 additions & 8 deletions app/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,62 @@
from .io import write_metadata
from .config import CFG
from .utils import gen_code

from .utils import logging

def routing(ep):
"""Unify the routing to reduce repetition"""
route_debug = CFG['route_debug']
if route_debug:
logging.info(f'Routing request for: {ep}')
info = dict(
workerId = request.args.get('PROLIFIC_PID'), # Prolific metadata
assignmentId = request.args.get('SESSION_ID'), # Prolific metadata
hitId = request.args.get('STUDY_ID'), # Prolific metadata
subId = gen_code(24), # NivTurk metadata
address = request.remote_addr, # NivTurk metadata
user_agent = request.user_agent.string.lower(), # User metadata
)

if route_debug:
logging.info('Info contents')
for k,v in info.items():
logging.info(f"{k}: {v}")
logging.info('Session contents')
for k,v in session.items():
logging.info(f"{k}: {v}")
disallowed_agent = any([device in info['user_agent'] for device in CFG['disallowed_agents']])
allowed_agent = any([device in info['user_agent'] for device in CFG['allowed_agents']])

# Case 1: workerId absent from URL.
try:
h_workerId = blake2b(info['workerId'].encode(), digest_size=24).hexdigest()
except AttributeError:
if route_debug:
logging.info('Failed case 1, no PROLIFIC_PID in url')
## Redirect participant to error (missing workerId).
return redirect(url_for('error.error', errornum=1000))

# complete might be written to metadata without updating session
# because task uploading has no session
if h_workerId in os.listdir(CFG['meta']):

## Parse log file.
with open(os.path.join(CFG['meta'], h_workerId), 'r') as f:
logs = f.read()

# Grab str fields from logs
fields = [
'complete',
]

for field in fields:
re_res = re.search(f'\t{field}\t(.*)\n', logs)
if re_res:
session[field] = re_res.group(1)

# Case 2: mobile / tablet / game console user.
# Not a terminal error because we want the user to be able to try again from a different device
if disallowed_agent or not allowed_agent:

if route_debug:
logging.info(f'Failed case 2, agent {info["user_agent"]} not allowed')
# Redirect participant to error (platform error).
return redirect(url_for('error.error', errornum=1001))

Expand Down Expand Up @@ -83,7 +111,7 @@ def routing(ep):
]

for field in bool_fields:
re_res = re.search(f'{field}\t(.*)\n', logs)
re_res = re.search(f'\t{field}\t(.*)\n', logs)
if re_res and re_res.group(1) == 'True':
info[field] = True # consent = true
elif re_res and re_res.group(1) == 'False':
Expand All @@ -99,7 +127,7 @@ def routing(ep):
]

for field in fields:
re_res = re.search(f'{field}\t(.*)\n', logs)
re_res = re.search(f'\t{field}\t(.*)\n', logs)
if re_res:
info[field] = re_res.group(1)

Expand All @@ -115,7 +143,6 @@ def routing(ep):
'hitId',
'assignmentId',
'subId',
'address',
'user_agent',
'platform'
], 'w')
Expand All @@ -136,7 +163,6 @@ def routing(ep):
'hitId',
'assignmentId',
'subId',
'address',
'user_agent',
'platform'
], 'w')
Expand Down
37 changes: 19 additions & 18 deletions app/taskstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .io import write_metadata, initialize_taskdata
from .config import CFG
from .routing import routing
from .utils import make_download
from .utils import edit_exe_worker_id, edit_app_worker_id
from hashlib import blake2b


Expand All @@ -25,20 +25,21 @@ def taskstart():
"""Present download to participant."""

rres = routing('taskstart')
h_workerId = blake2b(session['workerId'].encode(), digest_size=24).hexdigest()
supreme_subid = current_app.config['SUPREME_serializer'].dumps(h_workerId)
win_dlpath = os.path.join(CFG['download'], 'win_' + str(session['subId']) + '.exe')
mac_dlpath = os.path.join(CFG['download'], 'mac_' + str(session['subId']) + '.app')
make_download(supreme_subid, win_dlpath, 'windows')
make_download(supreme_subid, mac_dlpath, 'mac')
session['dlready'] = True
write_metadata(session, ['dlready'], 'a')
initialize_taskdata(session)
if rres is None:
h_workerId = blake2b(session['workerId'].encode(), digest_size=24).hexdigest()
supreme_subid = current_app.config['SUPREME_serializer'].dumps(h_workerId)
if 'dlready' not in session or not session['dlready']:
win_dlpath = os.path.join(CFG['download'], 'win_' + str(session['subId']) + '.exe')
mac_dlpath = os.path.join(CFG['download'], 'mac_' + str(session['subId']) + '.dmg')
edit_exe_worker_id(exe_file_path=CFG['base_exe'], new_worker_id=supreme_subid, output_file_path=win_dlpath)
edit_app_worker_id(app_path=CFG['base_app'], new_worker_id=supreme_subid, output_dmg_path=mac_dlpath)
session['dlready'] = True
write_metadata(session, ['dlready'], 'a')
initialize_taskdata(session)

mac_link = url_for('taskstart.download_mac')
win_link = url_for('taskstart.download_win')
mac_link = url_for('taskstart.download_mac', **request.args)
win_link = url_for('taskstart.download_win', **request.args)

if rres is None:
return render_template('taskstart.html',
platform=session['platform'],
mac_link=mac_link,
Expand All @@ -48,19 +49,19 @@ def taskstart():

@bp.route('/download/mac')
def download_mac():
dlpath = os.path.join(CFG['download'], 'mac_' + str(session['subId']) + '.app')
dlpath = os.path.join(CFG['download'], 'mac_' + str(session['subId']) + '.dmg')

session['dlstarted'] = True
write_metadata(session, ['dlstarted'], 'a')
if os.path.exists(dlpath) and session['dlready']:
return send_file(
dlpath,
as_attachment=True,
download_name='CogMood_task',
mimetype="inode/directory"
download_name='SUPREME.dmg',
mimetype="application/octet-stream"
)
else:
return redirect(url_for('task.task', **request.args))
return redirect(url_for('taskstart.taskstart', **request.args))

@bp.route('/download/win')
def download_win():
Expand All @@ -76,4 +77,4 @@ def download_win():
mimetype="application/vnd.microsoft.portable-executable"
)
else:
return redirect(url_for('task.task', **request.args))
return redirect(url_for('taskstart.taskstart', **request.args))
2 changes: 2 additions & 0 deletions app/templates/taskstart.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ <h3>Windows Instructions:</h3>
</div>

<!-- macOS Instructions -->
<!-- TODO: update with system settings instructions -->
<!-- TODO: update with message about how long it takes to start -->
<div id="macos-instructions" class="instructions">
<h3>macOS Instructions:</h3>
<button class="download-button" onclick="window.location.href='{{ mac_link }}'">Download for macOS</button>
Expand Down
6 changes: 3 additions & 3 deletions app/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def test_survey_complete(url, server, page: Page, request):
page.get_by_role("button", name="Complete").click()
else:
page.get_by_role("button", name="Next").click()
expect(page).to_have_url(f'{url}taskstart?PROLIFIC_PID={workerId}')
expect(page).to_have_url(f'{url}taskstart?PROLIFIC_PID={workerId}', timeout=10000)

if server:
# get saved data and compare to expectation
Expand Down Expand Up @@ -254,8 +254,8 @@ def test_taskcontrol(url, server, loadtest, ignore_https_errors, page: Page, req
page.get_by_role("button", name="Complete").click()
else:
page.get_by_role("button", name="Next").click()
expect(page).to_have_url(f'{url}taskstart?PROLIFIC_PID={workerId}')

expect(page).to_have_url(f'{url}taskstart?PROLIFIC_PID={workerId}', timeout=10000)
page.get_by_role("button").click()
# mock task communication with server
cfg = configparser.ConfigParser()
cfg.read(os.path.join(ROOT_DIR, 'app.ini'))
Expand Down
Loading