Skip to content

Commit 428df0c

Browse files
authored
Merge pull request #7 from compmem/collect_username
add custom_startup code for collecting username and code
2 parents aeb0486 + e22919a commit 428df0c

File tree

5 files changed

+138
-17
lines changed

5 files changed

+138
-17
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,7 @@ dmypy.json
131131
.pyre/
132132

133133
# MacOS
134-
.DS_Store
134+
.DS_Store
135+
136+
#pycharm
137+
.idea

config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66

77
CURRENT_OS: str = platform.system()
88
API_BASE_URL: str = 'https://mlc.nimh.nih.gov/cogmood'
9+
#API_BASE_URL='http://127.0.0.1:5000'
10+
API_SALT: str = 'SALT'
911
VERIFY: bool = False
1012
RUNNING_FROM_EXECUTABLE: bool = getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS')
13+
# WORKER_ID_SOURCE valid values = ['USER', 'EXECUTABLE']
14+
WORKER_ID_SOURCE = "USER"
1115
# WORKER_ID_PLACEHOLDER_VALUE is the placeholder value assigned to the WorkerID field
1216
# in the executables when we build them. It should be replaced by actual ID when
1317
# the executable is prepared for distribution.

custom_startup.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from smile.common import Experiment, Log, Wait, Func, UntilDone, \
2+
Label, Loop, If, Elif, Else, KeyPress, Ref, \
3+
Parallel, Slider, Serial, UpdateWidget, Debug, Meanwhile, While, Subroutine
4+
from smile.video import Rectangle, TextInput, Button, ButtonPress
5+
from smile.mouse import MouseCursor
6+
from smile.startup import (
7+
INFO_WIDTH,
8+
INFO_HEIGHT,
9+
INFO_OUTLINE_COLOR,
10+
INFO_COLOR,
11+
INFO_FONT_SIZE,
12+
INFO_BUTTON_HEIGHT,
13+
INFO_BUTTON_WIDTH,
14+
TEXT_INPUT_WIDTH,
15+
TEXT_INPUT_HEIGHT
16+
)
17+
from smile.scale import scale as s
18+
from hashlib import blake2b
19+
20+
import config as CogBatt_config
21+
22+
def _validate_code(exp):
23+
worker_id = exp._subject
24+
code = exp.get_var('_code')
25+
expected_code = blake2b(worker_id.encode(), digest_size=4, salt=CogBatt_config.API_SALT.encode()).hexdigest()[:4]
26+
Debug(code=code, expected_code=expected_code, invalid=code!=expected_code)
27+
exp.set_var('code_invalid', code != expected_code)
28+
29+
@Subroutine
30+
def InputSubject(self):
31+
with Parallel():
32+
with Parallel(blocking=False):
33+
MouseCursor()
34+
recOut = Rectangle(width=s(INFO_WIDTH) + s(20),
35+
height=s(INFO_HEIGHT) + s(20),
36+
color=INFO_OUTLINE_COLOR)
37+
recin = Rectangle(width=s(INFO_WIDTH),
38+
height=s(INFO_HEIGHT),
39+
color=INFO_COLOR)
40+
lbl = Label(text=CogBatt_config.EXP_NAME, center_x=recin.center_x,
41+
top=recin.top - s(10),
42+
font_size=s(INFO_FONT_SIZE))
43+
idIn = TextInput(width=s(TEXT_INPUT_WIDTH),
44+
height=s(TEXT_INPUT_HEIGHT),
45+
font_size=s(INFO_FONT_SIZE),
46+
center_x=recin.center_x,
47+
top=lbl.bottom - s(20),
48+
multiline=False,
49+
text="",
50+
disabled=False,
51+
hint_text="Prolific Worker ID",
52+
write_tab=False)
53+
codeIn = TextInput(width=s(TEXT_INPUT_WIDTH),
54+
height=s(TEXT_INPUT_HEIGHT),
55+
font_size=s(INFO_FONT_SIZE),
56+
center_x=recin.center_x,
57+
top=lbl.bottom - s(80),
58+
multiline=False,
59+
text="",
60+
disabled=False,
61+
hint_text="4 digit task code",
62+
write_tab=False)
63+
bc = Button(text="Continue", font_size=s(INFO_FONT_SIZE),
64+
height=s(INFO_BUTTON_HEIGHT),
65+
width=s(INFO_BUTTON_WIDTH),
66+
right=recin.right - s(20),
67+
bottom=recin.bottom + s(20),
68+
name="C",
69+
background_normal="",
70+
background_color=INFO_OUTLINE_COLOR,
71+
disabled=True)
72+
with Serial():
73+
with While(
74+
(Ref.object(codeIn.text).__len__() < 4)
75+
or (Ref(str, idIn.text) == '')
76+
):
77+
Wait(0.1)
78+
bc.disabled = False
79+
80+
bp = ButtonPress(buttons=[bc])
81+
with If(
82+
(bp.pressed == "C")
83+
):
84+
Func(self.exp._change_smile_subj, Ref.object(idIn.text).lower().strip())
85+
Func(self.exp.set_var, '_code', Ref.object(codeIn.text).lower().strip())
86+
Func(_validate_code, Ref.object(self.exp))

main.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
# Smile imports
55
from smile.common import Experiment, Log, Wait, Func, UntilDone, \
66
Label, Loop, If, Elif, Else, KeyPress, Ref, \
7-
Parallel, Slider, Serial, UpdateWidget, Debug, Meanwhile
8-
from smile.clock import clock
7+
Parallel, Slider, Serial, UpdateWidget, Debug, Meanwhile, While
98
from smile.scale import scale as s
9+
from custom_startup import InputSubject
1010
# from android.permissions import request_permissions, Permission
1111
# request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE])
1212
from kivy.resources import resource_add_path
@@ -64,11 +64,6 @@
6464
WRK_DIR = sys._MEIPASS
6565
resource_add_path(WRK_DIR)
6666

67-
retrieved_worker_id = retrieve_worker_id()
68-
69-
tasks_from_api = get_blocks_to_run(retrieved_worker_id['content'])
70-
number_of_tasks = 0 if tasks_from_api['status'] == 'error' else len(tasks_from_api['content'])
71-
7267

7368
# Initialize the SMILE experiment.
7469
exp = Experiment(name=CogBatt_config.EXP_NAME,
@@ -77,9 +72,27 @@
7772
Touch=False, local_crashlog=True,
7873
cmd_traceback=False, data_dir=WRK_DIR,
7974
working_dir=WRK_DIR)
75+
exp._code = ''
76+
if CogBatt_config.WORKER_ID_SOURCE == 'EXECUTABLE':
77+
retrieved_worker_id = retrieve_worker_id()
78+
tasks_from_api = get_blocks_to_run(retrieved_worker_id['content'])
79+
number_of_tasks = 0 if tasks_from_api['status'] == 'error' else len(tasks_from_api['content'])
80+
81+
exp.tasks_from_api = tasks_from_api
82+
exp.worker_id_dict = retrieved_worker_id
83+
elif CogBatt_config.WORKER_ID_SOURCE == 'USER':
84+
InputSubject()
85+
tasks_from_api = Func(get_blocks_to_run, Ref.object(exp)._subject, Ref.object(exp).get_var('_code')).result
86+
with If(tasks_from_api['status'] == 'error'):
87+
number_of_tasks = 0
88+
with Else():
89+
number_of_tasks = tasks_from_api['content'].__len__()
90+
exp.tasks_from_api = tasks_from_api
91+
exp.worker_id_dict = {"status": "success", "content": Ref.object(exp)._subject}
92+
else:
93+
raise NotImplementedError
94+
8095

81-
exp.tasks_from_api = tasks_from_api
82-
exp.worker_id_dict = retrieved_worker_id
8396

8497
with Parallel():
8598
with Serial(blocking=False):
@@ -89,8 +102,13 @@
89102
author=version.__author__,
90103
date_time=version.__date__,
91104
email=version.__email__)
92-
93105
Wait(.5)
106+
with If(Ref.object(exp).get_var('code_invalid')):
107+
error_screen(error='Invalid task code: ' + Ref(str, exp._code),
108+
message='You entered an incorrect task code, please double check the code '
109+
'listed on the website and try again. If it still does not work '
110+
'please contact Dylan Nielson at [email protected].'
111+
)
94112

95113
with If(CogBatt_config.RUNNING_FROM_EXECUTABLE):
96114
# Handles case where retrieval of worker id fails
@@ -196,7 +214,8 @@
196214
block_name=exp.task_name + '_' + Ref(str, exp.block_number),
197215
data_directory=Ref.object(
198216
exp)._session_dir,
199-
slog_file_name='log_'+exp.task_name+'_'+'0.slog')
217+
slog_file_name='log_'+exp.task_name+'_'+'0.slog',
218+
code=Ref.object(exp).get_var('_code'))
200219
Wait(3)
201220

202221
# Error screen for failed upload

utils.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import requests
55
import logging
66
import time
7-
from config import API_BASE_URL, RUNNING_FROM_EXECUTABLE, CURRENT_OS, VERIFY
7+
from config import API_BASE_URL, RUNNING_FROM_EXECUTABLE, CURRENT_OS, VERIFY, WORKER_ID_SOURCE
88
from hashlib import blake2b
99
from io import BytesIO
1010
from pathlib import Path
@@ -17,18 +17,22 @@
1717
format='%(asctime)s - %(levelname)s - %(message)s')
1818

1919

20-
def get_blocks_to_run(worker_id: str) -> list[str] | dict[str, str]:
20+
def get_blocks_to_run(worker_id: str, code: str | None = None) -> list[str] | dict[str, str]:
2121
"""
2222
Sends a GET request to retrieve the list of blocks that are yet to be run by the worker.
2323
2424
Args:
2525
worker_id (str): The unique identifier for the worker whose blocks are being fetched.
26+
code (str): Optional verifcation code if worker_id_source is user
2627
2728
Returns:
2829
dict: A dictionary with 'status' as success or error, and 'content' containing either the blocks list or an error message.
2930
"""
3031
url = f'{API_BASE_URL}/taskcontrol'
31-
params = {'worker_id': worker_id}
32+
if WORKER_ID_SOURCE == 'EXECUTABLE':
33+
params = {'worker_id': worker_id}
34+
elif WORKER_ID_SOURCE == 'USER':
35+
params = {'worker_id': worker_id, 'code':code}
3236

3337
try:
3438
response = requests.get(url, params=params, verify=VERIFY)
@@ -78,7 +82,7 @@ def hash_file(file_obj):
7882
return hash_blake.hexdigest()
7983

8084

81-
def upload_block(worker_id: str, block_name: str, data_directory: str, slog_file_name: str) -> dict[str, str]:
85+
def upload_block(worker_id: str, block_name: str, data_directory: str, slog_file_name: str, code: str | None = None) -> dict[str, str]:
8286
"""
8387
Sends a POST request to upload a completed block along with its checksum and the associated zipped file.
8488
Uses the config.API_BASE_URL to build the URL.
@@ -88,6 +92,7 @@ def upload_block(worker_id: str, block_name: str, data_directory: str, slog_file
8892
block_name (str): The name of the block being uploaded, typically in the format "taskname_runnumber".
8993
data_directory (str): The directory where the slog file is located.
9094
slog_file_name (str): The name of the slog file.
95+
code (str): Optional verifcation code if worker_id_source is user.
9196
9297
Behavior:
9398
- Zips the slog file.
@@ -119,7 +124,10 @@ def upload_block(worker_id: str, block_name: str, data_directory: str, slog_file
119124
current_time_ms = int(time.time() * 1000)
120125
zip_file_name_with_timestamp = f'{block_name}_{current_time_ms}.zip'
121126

122-
params = {'worker_id': worker_id}
127+
if WORKER_ID_SOURCE == 'EXECUTABLE':
128+
params = {'worker_id': worker_id}
129+
elif WORKER_ID_SOURCE == 'USER':
130+
params = {'worker_id': worker_id, 'code': code}
123131
data = {'block_name': block_name, 'checksum': checksum}
124132
files = {'file': (zip_file_name_with_timestamp, zip_buffer, 'application/zip')}
125133

@@ -243,6 +251,7 @@ def _read_exe_worker_id() -> dict[str, str]:
243251
return {"status": "error", "content": str(e)}
244252

245253

254+
246255
if __name__ == "__main__":
247256
upload_block(worker_id='123456',
248257
block_name='flkr_1',

0 commit comments

Comments
 (0)