diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/1/1707937420876-663375665-avatar-tasks_list.png b/data_for_review/data/runs/NO_PROJECT/1/1/1/1707937420876-663375665-avatar-tasks_list.png
new file mode 100644
index 000000000..f50986f2c
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/1/1/1707937420876-663375665-avatar-tasks_list.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/1/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/1/1/agent_meta.json
new file mode 100644
index 000000000..701358773
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/1/1/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707937389.27229, "task_end": 1707937421.1177907, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/1/state.json b/data_for_review/data/runs/NO_PROJECT/1/1/1/state.json
new file mode 100644
index 000000000..97901ea25
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/1/1/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "
Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "Qwe", "name_last": "Qwe", "email": "qwe@mephisto.ai", "country": "USA", "language": ["es", "ch"], "bio": "", "skills": {"react": true, "javascript": true, "python": false, "sql": false}, "kids": "1", "avatar": {"lastModified": 1706823899460, "name": "tasks_list.png", "size": 24026, "type": "image/png"}, "resume": "", "motto": "dasdasdasdasdas", "files": [{"fieldname": "avatar", "originalname": "tasks_list.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707937420876-663375665-avatar-tasks_list.png", "path": "/tmp/1707937420876-663375665-avatar-tasks_list.png", "size": 24026}]}, "requests": [{"uuid": "7daac7d7-fdb0-4a7d-bc92-958259a49daf", "target": "getMultiplePresignedUrls", "args_json": "[\"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png\"]", "response_json": null, "timestamp": 1707937390.490068}, {"uuid": "64546bc2-86cb-4d1e-8055-c924b0e668bb", "target": "7daac7d7-fdb0-4a7d-bc92-958259a49daf", "args_json": null, "response_json": "[[\"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png\", \"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png?response-content-type=image%2Fpng&AWSAccessKeyId=AKIAS56YDPVYGCIDIE5K&Signature=YsLmUFwnormybZf8U0pTR3on1%2BA%3D&Expires=1707937510\"]]", "timestamp": 1707937390.5208004}]}, "start_time": 1707937389.27229, "end_time": 1707937421.1177907}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/2/1707937511496-946753397-avatar-submission_reject_dialog.png b/data_for_review/data/runs/NO_PROJECT/1/1/2/1707937511496-946753397-avatar-submission_reject_dialog.png
new file mode 100644
index 000000000..f3345b0de
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/1/2/1707937511496-946753397-avatar-submission_reject_dialog.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/2/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/1/2/agent_meta.json
new file mode 100644
index 000000000..47d8bfc24
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/1/2/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707937484.8482091, "task_end": 1707937511.707398, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/2/state.json b/data_for_review/data/runs/NO_PROJECT/1/1/2/state.json
new file mode 100644
index 000000000..7cabad516
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/1/2/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "Asdf", "name_last": "Asdf", "email": "asdf@mephisto.ai", "country": "CAN", "language": ["fr", "es"], "bio": "", "skills": {"react": true, "javascript": true, "python": false, "sql": false}, "kids": ">=3", "avatar": {"lastModified": 1706820157399, "name": "submission_reject_dialog.png", "size": 23982, "type": "image/png"}, "resume": "", "motto": "asdf asdf asdf", "files": [{"fieldname": "avatar", "originalname": "submission_reject_dialog.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707937511496-946753397-avatar-submission_reject_dialog.png", "path": "/tmp/1707937511496-946753397-avatar-submission_reject_dialog.png", "size": 23982}]}, "requests": [{"uuid": "e782da6d-0cbb-4091-9e3a-a3eb2df49ead", "target": "getMultiplePresignedUrls", "args_json": "[\"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png\"]", "response_json": null, "timestamp": 1707937486.1177983}, {"uuid": "f78fcee9-1e99-4fff-8906-d9e1e6c3d972", "target": "e782da6d-0cbb-4091-9e3a-a3eb2df49ead", "args_json": null, "response_json": "[[\"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png\", \"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png?response-content-type=image%2Fpng&AWSAccessKeyId=AKIAS56YDPVYGCIDIE5K&Signature=OQL2s7qBOT9nqivmNwU38HgpgYE%3D&Expires=1707937606\"]]", "timestamp": 1707937486.1474278}]}, "start_time": 1707937484.8482091, "end_time": 1707937511.707398}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/3/1707937630512-896537233-avatar-pg_for_prolific.png b/data_for_review/data/runs/NO_PROJECT/1/1/3/1707937630512-896537233-avatar-pg_for_prolific.png
new file mode 100644
index 000000000..f741cff56
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/1/3/1707937630512-896537233-avatar-pg_for_prolific.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/3/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/1/3/agent_meta.json
new file mode 100644
index 000000000..14bbb84a6
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/1/3/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707937564.9948661, "task_end": 1707937631.8736277, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/3/state.json b/data_for_review/data/runs/NO_PROJECT/1/1/3/state.json
new file mode 100644
index 000000000..f6e3e20d6
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/1/3/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "iop", "name_last": "iop", "email": "iop@mephisto.ai", "country": "CAN", "language": ["fr", "es"], "bio": "", "skills": {"react": true, "javascript": true, "python": true, "sql": false}, "kids": "2", "avatar": {"lastModified": 1689125356688, "name": "pg_for_prolific.png", "size": 33390, "type": "image/png"}, "resume": "", "motto": "uiopiopiopiopiopiop", "files": [{"fieldname": "avatar", "originalname": "pg_for_prolific.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707937630512-896537233-avatar-pg_for_prolific.png", "path": "/tmp/1707937630512-896537233-avatar-pg_for_prolific.png", "size": 33390}]}, "requests": [{"uuid": "e31eda2d-43e2-47fa-b4f7-7e5622df9df1", "target": "getMultiplePresignedUrls", "args_json": "[\"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png\"]", "response_json": null, "timestamp": 1707937566.2293427}, {"uuid": "5a1641e9-dfc7-4477-aea2-9f940d637741", "target": "e31eda2d-43e2-47fa-b4f7-7e5622df9df1", "args_json": null, "response_json": "[[\"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png\", \"https://dev-alented-private.s3.amazonaws.com/mephisto/help_page.png?response-content-type=image%2Fpng&AWSAccessKeyId=AKIAS56YDPVYGCIDIE5K&Signature=ITYzSZ4nvlFJyWGbQtFhoGyryHo%3D&Expires=1707937686\"]]", "timestamp": 1707937566.2434895}]}, "start_time": 1707937564.9948661, "end_time": 1707937631.8736277}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/1/assign_data.json b/data_for_review/data/runs/NO_PROJECT/1/1/assign_data.json
new file mode 100644
index 000000000..1027b2272
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/1/assign_data.json
@@ -0,0 +1 @@
+{"shared": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "unit_data": [{}, {}, {}]}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/4/1707937721367-515628869-avatar-same_ip_ban.png b/data_for_review/data/runs/NO_PROJECT/1/2/4/1707937721367-515628869-avatar-same_ip_ban.png
new file mode 100644
index 000000000..98d2267a3
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/2/4/1707937721367-515628869-avatar-same_ip_ban.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/4/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/2/4/agent_meta.json
new file mode 100644
index 000000000..954d93980
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/2/4/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707937689.5415833, "task_end": 1707937722.62274, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/4/state.json b/data_for_review/data/runs/NO_PROJECT/1/2/4/state.json
new file mode 100644
index 000000000..933d078d7
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/2/4/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "qwe", "name_last": "qwe", "email": "qwe@mephisto.ai", "country": "USA", "language": ["fr", "es"], "bio": "", "skills": {"react": false, "javascript": true, "python": true, "sql": false}, "kids": "2", "avatar": {"lastModified": 1687465008705, "name": "same_ip_ban.png", "size": 37751, "type": "image/png"}, "resume": "", "motto": "sdfv sfghvetyhvetybhjetyjbet", "files": [{"fieldname": "avatar", "originalname": "same_ip_ban.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707937721367-515628869-avatar-same_ip_ban.png", "path": "/tmp/1707937721367-515628869-avatar-same_ip_ban.png", "size": 37751}]}, "requests": [{"uuid": "4e6fa5f5-8b93-49f1-8a5e-85f8a5867e6a", "target": "getMultiplePresignedUrls", "args_json": "[\"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png\"]", "response_json": null, "timestamp": 1707937690.769792}, {"uuid": "cf1d0785-bcf0-4f69-a3d3-a2b469eecdef", "target": "4e6fa5f5-8b93-49f1-8a5e-85f8a5867e6a", "args_json": null, "response_json": "[[\"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png\", \"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png?response-content-type=image%2Fpng&AWSAccessKeyId=AKIAS56YDPVYGCIDIE5K&Signature=bUgM4sZ5HwFBgmMkNdynj3LlRFQ%3D&Expires=1707937810\"]]", "timestamp": 1707937690.8011067}]}, "start_time": 1707937689.5415833, "end_time": 1707937722.62274}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/5/1707937835758-56050741-avatar-tasks_list.png b/data_for_review/data/runs/NO_PROJECT/1/2/5/1707937835758-56050741-avatar-tasks_list.png
new file mode 100644
index 000000000..f50986f2c
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/2/5/1707937835758-56050741-avatar-tasks_list.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/5/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/2/5/agent_meta.json
new file mode 100644
index 000000000..92dd042aa
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/2/5/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707937797.4434538, "task_end": 1707937839.2385657, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/5/state.json b/data_for_review/data/runs/NO_PROJECT/1/2/5/state.json
new file mode 100644
index 000000000..278bd7c3a
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/2/5/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "ii", "name_last": "iii", "email": "iii@mephisto.ai", "country": "USA", "language": ["fr", "es"], "bio": "", "skills": {"react": true, "javascript": true, "python": false, "sql": false}, "kids": "1", "avatar": {"lastModified": 1706823899460, "name": "tasks_list.png", "size": 24026, "type": "image/png"}, "resume": "", "motto": "dfgaergsrtgwrthwrth", "files": [{"fieldname": "avatar", "originalname": "tasks_list.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707937835758-56050741-avatar-tasks_list.png", "path": "/tmp/1707937835758-56050741-avatar-tasks_list.png", "size": 24026}]}, "requests": [{"uuid": "ff256c1e-0ded-42a8-b05c-da8880a4f1a2", "target": "getMultiplePresignedUrls", "args_json": "[\"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png\"]", "response_json": null, "timestamp": 1707937800.4901805}, {"uuid": "ef7bf7fc-8a96-4e6e-8f49-21f366b13d7b", "target": "ff256c1e-0ded-42a8-b05c-da8880a4f1a2", "args_json": null, "response_json": "[[\"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png\", \"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png?response-content-type=image%2Fpng&AWSAccessKeyId=AKIAS56YDPVYGCIDIE5K&Signature=w1ZSZRF%2FrO4LnQHFVIqqUC4m6GU%3D&Expires=1707937920\"]]", "timestamp": 1707937800.5073702}]}, "start_time": 1707937797.4434538, "end_time": 1707937839.2385657}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/6/1707938237933-549751943-avatar-submission_approve_dialog.png b/data_for_review/data/runs/NO_PROJECT/1/2/6/1707938237933-549751943-avatar-submission_approve_dialog.png
new file mode 100644
index 000000000..729a00262
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/2/6/1707938237933-549751943-avatar-submission_approve_dialog.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/6/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/2/6/agent_meta.json
new file mode 100644
index 000000000..ea40b9567
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/2/6/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707938190.8573258, "task_end": 1707938243.644762, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/6/state.json b/data_for_review/data/runs/NO_PROJECT/1/2/6/state.json
new file mode 100644
index 000000000..775a0a523
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/2/6/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "opop", "name_last": "opop", "email": "opo@mephisto.ai", "country": "USA", "language": ["en", "es"], "bio": "", "skills": {"react": true, "javascript": false, "python": true, "sql": false}, "kids": "2", "avatar": {"lastModified": 1706820056492, "name": "submission_approve_dialog.png", "size": 30807, "type": "image/png"}, "resume": "", "motto": "lkj lkj l kj lkj lkj ", "files": [{"fieldname": "avatar", "originalname": "submission_approve_dialog.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707938237933-549751943-avatar-submission_approve_dialog.png", "path": "/tmp/1707938237933-549751943-avatar-submission_approve_dialog.png", "size": 30807}]}, "requests": [{"uuid": "a5a407c8-7e7b-4ffd-ba58-2a2e86616fb3", "target": "getMultiplePresignedUrls", "args_json": "[\"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png\"]", "response_json": null, "timestamp": 1707938194.1629834}, {"uuid": "f643f4f8-c812-4a48-ab16-10434138f189", "target": "a5a407c8-7e7b-4ffd-ba58-2a2e86616fb3", "args_json": null, "response_json": "[[\"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png\", \"https://dev-alented-private.s3.amazonaws.com/mephisto/logo_color.png?response-content-type=image%2Fpng&AWSAccessKeyId=AKIAS56YDPVYGCIDIE5K&Signature=%2BI2eiCJOFfqwHaqAnnzRKHYXVfc%3D&Expires=1707938314\"]]", "timestamp": 1707938194.1817126}]}, "start_time": 1707938190.8573258, "end_time": 1707938243.644762}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/2/assign_data.json b/data_for_review/data/runs/NO_PROJECT/1/2/assign_data.json
new file mode 100644
index 000000000..332dcf571
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/2/assign_data.json
@@ -0,0 +1 @@
+{"shared": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "unit_data": [{}, {}, {}]}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/7/1707938323101-640052012-avatar-layout2.png b/data_for_review/data/runs/NO_PROJECT/1/3/7/1707938323101-640052012-avatar-layout2.png
new file mode 100644
index 000000000..ed9af6a0c
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/3/7/1707938323101-640052012-avatar-layout2.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/7/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/3/7/agent_meta.json
new file mode 100644
index 000000000..b3fea5994
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/3/7/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707938280.6005406, "task_end": 1707938323.369965, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/7/state.json b/data_for_review/data/runs/NO_PROJECT/1/3/7/state.json
new file mode 100644
index 000000000..feeff28bf
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/3/7/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "cvbcvb cvb ", "name_last": "cvbcvb vcb ", "email": "cvb@mephisto.ai", "country": "CAN", "language": ["fr", "es"], "bio": "", "skills": {"react": false, "javascript": true, "python": true, "sql": true}, "kids": "0", "avatar": {"lastModified": 1694795937320, "name": "layout2.png", "size": 39728, "type": "image/png"}, "resume": "", "motto": "dfgbsfgh dfg hrt ntyd nndy jnt", "files": [{"fieldname": "avatar", "originalname": "layout2.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707938323101-640052012-avatar-layout2.png", "path": "/tmp/1707938323101-640052012-avatar-layout2.png", "size": 39728}]}, "requests": [{"uuid": "2bf9a843-5289-4220-94f4-fd59fb2fe053", "target": "getMultiplePresignedUrls", "args_json": "[\"https://dev-alented-private.s3.amazonaws.com/mephisto/shine vector.png\"]", "response_json": null, "timestamp": 1707938281.8222702}, {"uuid": "dfd70ca1-c132-41ae-937c-a196c7f6d754", "target": "2bf9a843-5289-4220-94f4-fd59fb2fe053", "args_json": null, "response_json": "[[\"https://dev-alented-private.s3.amazonaws.com/mephisto/shine vector.png\", \"https://dev-alented-private.s3.amazonaws.com/mephisto/shine%20vector.png?response-content-type=image%2Fpng&AWSAccessKeyId=AKIAS56YDPVYGCIDIE5K&Signature=BcyEM7zoho2A2pi3MzjbOr56Dsc%3D&Expires=1707938401\"]]", "timestamp": 1707938281.8673115}]}, "start_time": 1707938280.6005406, "end_time": 1707938323.369965}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/8/1707938404252-201181976-avatar-submission_reject_dialog.png b/data_for_review/data/runs/NO_PROJECT/1/3/8/1707938404252-201181976-avatar-submission_reject_dialog.png
new file mode 100644
index 000000000..f3345b0de
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/3/8/1707938404252-201181976-avatar-submission_reject_dialog.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/8/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/3/8/agent_meta.json
new file mode 100644
index 000000000..62d3ff7f2
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/3/8/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707938378.3749745, "task_end": 1707938412.4433434, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/8/state.json b/data_for_review/data/runs/NO_PROJECT/1/3/8/state.json
new file mode 100644
index 000000000..53cf773cf
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/3/8/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "rtyrty", "name_last": "rtyrty", "email": "rty@mephisto.ai", "country": "CAN", "language": ["fr", "es"], "bio": "", "skills": {"react": true, "javascript": true, "python": false, "sql": false}, "kids": "2", "avatar": {"lastModified": 1706820157399, "name": "submission_reject_dialog.png", "size": 23982, "type": "image/png"}, "resume": "", "motto": "rtyrtyrtytrytr", "files": [{"fieldname": "avatar", "originalname": "submission_reject_dialog.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707938404252-201181976-avatar-submission_reject_dialog.png", "path": "/tmp/1707938404252-201181976-avatar-submission_reject_dialog.png", "size": 23982}]}, "requests": [{"uuid": "0bb77f1e-4798-47a9-9ffb-8584f86b94ed", "target": "getMultiplePresignedUrls", "args_json": "[\"https://dev-alented-private.s3.amazonaws.com/mephisto/shine vector.png\"]", "response_json": null, "timestamp": 1707938379.602152}, {"uuid": "4d39da41-023c-47de-9b36-5f89c5114e9f", "target": "0bb77f1e-4798-47a9-9ffb-8584f86b94ed", "args_json": null, "response_json": "[[\"https://dev-alented-private.s3.amazonaws.com/mephisto/shine vector.png\", \"https://dev-alented-private.s3.amazonaws.com/mephisto/shine%20vector.png?response-content-type=image%2Fpng&AWSAccessKeyId=AKIAS56YDPVYGCIDIE5K&Signature=ywLfS9Flgup%2FzpjX%2BMQpYiJE4DE%3D&Expires=1707938499\"]]", "timestamp": 1707938379.6246803}]}, "start_time": 1707938378.3749745, "end_time": 1707938412.4433434}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/9/1707938614014-494757902-avatar-_task_without_controls.png b/data_for_review/data/runs/NO_PROJECT/1/3/9/1707938614014-494757902-avatar-_task_without_controls.png
new file mode 100644
index 000000000..3bc062d08
Binary files /dev/null and b/data_for_review/data/runs/NO_PROJECT/1/3/9/1707938614014-494757902-avatar-_task_without_controls.png differ
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/9/agent_meta.json b/data_for_review/data/runs/NO_PROJECT/1/3/9/agent_meta.json
new file mode 100644
index 000000000..f341ae166
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/3/9/agent_meta.json
@@ -0,0 +1 @@
+{"task_start": 1707938562.4529753, "task_end": 1707938634.8017304, "tips": null, "feedback": null}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/9/state.json b/data_for_review/data/runs/NO_PROJECT/1/3/9/state.json
new file mode 100644
index 000000000..60a655936
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/3/9/state.json
@@ -0,0 +1 @@
+{"inputs": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "outputs": {"final_submission": {"name_first": "fghgf", "name_last": "fghfgh", "email": "fghfgh@mephisto.ai", "country": "USA", "language": ["en", "fr"], "bio": "", "skills": {"react": true, "javascript": true, "python": false, "sql": false}, "kids": "2", "avatar": {"lastModified": 1680727145395, "name": "_task_without_controls.png", "size": 276402, "type": "image/png"}, "resume": "", "motto": "b giorjb dfgoi;bj tdio j;t", "files": [{"fieldname": "avatar", "originalname": "_task_without_controls.png", "encoding": "7bit", "mimetype": "image/png", "destination": "/tmp/", "filename": "1707938614014-494757902-avatar-_task_without_controls.png", "path": "/tmp/1707938614014-494757902-avatar-_task_without_controls.png", "size": 276402}]}, "requests": []}, "start_time": 1707938562.4529753, "end_time": 1707938634.8017304}
\ No newline at end of file
diff --git a/data_for_review/data/runs/NO_PROJECT/1/3/assign_data.json b/data_for_review/data/runs/NO_PROJECT/1/3/assign_data.json
new file mode 100644
index 000000000..af35555d5
--- /dev/null
+++ b/data_for_review/data/runs/NO_PROJECT/1/3/assign_data.json
@@ -0,0 +1 @@
+{"shared": {"form": {"title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.", "sections": [{"name": "section_about", "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
", "collapsable": false, "fieldsets": [{"title": "Personal information", "instruction": "", "rows": [{"fields": [{"help": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", "tooltip": "Your first name", "type": "input", "validators": {"required": true, "minLength": 2, "maxLength": 20}, "value": ""}, {"help": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": ""}], "help": "Please use your legal name"}, {"fields": [{"help": "We may contact you later at your Mephisto email for additional information", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", "tooltip": "Email address for Mephisto", "type": "email", "validators": {"required": true, "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]}, "value": ""}]}]}, {"title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [{"fields": [{"help": "Select country of your residence", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [{"label": "---", "value": ""}, {"label": "United States of America", "value": "USA"}, {"label": "Canada", "value": "CAN"}], "placeholder": "", "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": ""}, {"help": "Select language spoken in your local community", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [{"label": "English", "value": "en"}, {"label": "French", "value": "fr"}, {"label": "Spanish", "value": "es"}, {"label": "Chinese", "value": "ch"}], "placeholder": "", "tooltip": "Language", "type": "select", "validators": {"required": true, "minLength": 2, "maxLength": 3}, "value": ""}]}], "help": "This information will help us compile study statistics"}, {"title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [{"fields": [{"help": "", "id": "id_bio", "label": "Biography since age of 18", "name": "bio", "placeholder": "", "tooltip": "Your bio in a few paragraphs", "type": "textarea", "validators": {"required": false}, "value": ""}]}, {"fields": [{"help": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", "options": [{"checked": false, "label": "React", "value": "react"}, {"checked": true, "label": "JavaScript", "value": "javascript"}, {"checked": false, "label": "Python", "value": "python"}, {"checked": false, "label": "SQL", "value": "sql"}], "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": {"required": true, "minLength": 2, "maxLength": 3}}]}, {"fields": [{"help": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", "options": [{"checked": false, "label": "None", "value": "0"}, {"checked": false, "label": "One", "value": "1"}, {"checked": false, "label": "Two", "value": "2"}, {"checked": false, "label": "Three or more", "value": ">=3"}], "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true}}]}, {"fields": [{"help": "We only accept files in PNG, JPG, and JPEG formats.", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", "tooltip": "Your profile photo", "type": "file", "validators": {"required": true, "fileExtension": ["png", "jpg", "jpeg"]}, "value": ""}, {"help": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": ""}]}], "help": "Some additional details about your persona"}]}, {"name": "section_second", "title": "Second section", "instruction": "Example of another section", "initially_collapsed": true, "fieldsets": [{"title": "Motivation", "instruction": "", "rows": [{"fields": [{"id": "id_motto", "label": "Personal Motto", "name": "motto", "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}}], "help": "Please type in your favorite personal motto"}]}]}], "submit_button": {"instruction": "Please double-check if everything has been filled in correctly.", "text": "Submit", "tooltip": "Submit form"}}}, "unit_data": [{}, {}, {}]}
\ No newline at end of file
diff --git a/data_for_review/database.db b/data_for_review/database.db
new file mode 100644
index 000000000..2f63e4b7c
Binary files /dev/null and b/data_for_review/database.db differ
diff --git a/data_for_review/prolific/prolific.db b/data_for_review/prolific/prolific.db
new file mode 100644
index 000000000..cc646df9e
Binary files /dev/null and b/data_for_review/prolific/prolific.db differ
diff --git a/docker/ssh_known_hosts b/docker/ssh_known_hosts
index 64c145138..e69de29bb 100644
--- a/docker/ssh_known_hosts
+++ b/docker/ssh_known_hosts
@@ -1,4 +0,0 @@
-|1|WJ4PmT6+VNxRqvJK/McyqljC6Fg=|e4RzD3dL3YqFqOb7bCnAhO9PPrU= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJSKG3/u1suAx1rlqsjTB4ZSnuaFaHbG3IWXe2B3jW/YGJfpUGVdcOjBZ/My6uMLG2An0SLX21YMZHd0ncQajBc=
-|1|GicO64VSOj5Sf8rb9P/S4249bd0=|5hNyMZyXYc5fbADc23wkb1OVUqw= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAWS2J/iarr+yaaP+QMVGGjeP2EHh0o33RqsDnr6t2wXL1FmtAxAs47CjTMHkHKP5uZ+RkYFPHgVZhQD70Jyt/8=
-|1|f/DA8gbhaa9MZcZ8j/Yoa9l4qn8=|mT02a7Cxiu9O5CjW7yqoNaO/o6M= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOrD+gvoZj8GdwJcWQiCh/8jWmXIJ2fGMWbYzHB23K2X820PD7LGh7Z2jwz3evQA5E+Sk6qYWVV9oUVoXo3xXFY=
-|1|Sy5mlOuOps/Cfa543Yqdb07/SG4=|C4SJ0UXqz2AO5ZbiYA1ckqY6eJo= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHmhGHaLHLpz0mak1imyCuhZE3rTuJT+Zm7ofdWXgG8gZ9Xn+/jbP6Jvnf8K8ZnKyCv8GxRfj9lr4Wl+pfSro9o=
diff --git a/examples/README.md b/examples/README.md
index 454007a5a..b1ee47c04 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -5,6 +5,7 @@
-->
# Examples
+
The Mephisto example folders within contain some sample starter code and tasks to demonstrate potential workflows for setting up and working on new tasks.
Mephisto Tasks can be launched (each run is called TaskRun) with a single `docker-compose` command (you will need to have Docker [installed](https://docs.docker.com/engine/install/).)
@@ -72,11 +73,16 @@ A more complex example featuring worker-generated dynamic input: [mnist](/exampl
#### 3. Dynamic form-based task
-You can create and modify auto composing forms that we use in command `mephisto form_composer` ([README](/mephisto/generators/form_composer/README.md)) if it looks too easy for you.
+You can create and modify auto-composed form-based tasks using FormComposer example (its full specs are in this [README](/mephisto/generators/form_composer/README.md)).
+
+There are three FormComposer examples:
+
+##### 3.1. Simple form
-There are three examples:
+This is a single-version form containing no variable tokens.
-1. Simple example with already prepared task data config. Launch command:
+- Default config file: [example_local_mock.yaml](/examples/form_composer_demo/hydra_configs/conf/example_local_mock.yaml).
+- Launch command:
```shell
docker-compose -f docker/docker-compose.dev.yml run \
--build \
@@ -84,8 +90,15 @@ There are three examples:
--rm mephisto_dc \
python /mephisto/examples/form_composer_demo/run_task.py
```
+- Browser page (for the first task unit): [http://localhost:3001/?worker_id=x&assignment_id=1](http://localhost:3001/?worker_id=x&assignment_id=1).
+- You should see a Bootstrap-themed form with fields and sections corresponding to the form config in its [task_data.json](/examples/form_composer_demo/data/simple/task_data.json) file.
+
+##### 3.2. Dynamic form
-2. Dynamic example with already prepared all configs which you can play, regenerate them and see how easy you can make your own more complicated form. Launch command:
+Dynamic form means a multi-version form, where versions are generated by varying values of special tokens embedded into the form config.
+
+- Default config file: [dynamic_example_local_mock.yaml](/examples/form_composer_demo/hydra_configs/conf/dynamic_example_local_mock.yaml).
+- Launch command:
```shell
docker-compose -f docker/docker-compose.dev.yml run \
--build \
@@ -93,22 +106,31 @@ There are three examples:
--rm mephisto_dc \
python /mephisto/examples/form_composer_demo/run_task_dynamic.py
```
-Also, there are other dynamic commands to play with Prolific or MTurk, just change script name in command to `run_task_dynamic_ec2_prolific.py` and `run_task_dynamic_ec2_mturk_sandbox.py` accordingly.
-
-3. Dynamic example with already prepared all configs, but to show how to work with presigned S3 URLs.
-To make this example fully workable, you need to set AWS credentials as environment variables
-`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `S3_URL_EXPIRATION_MINUTES` (if you want to change default 1 hour) in file `docker/envs/env.dev`.
-Also, you need to change fake URLs in all configs on private ones from your own S3 storage.
-Or you can use commands to recreate all configs aotomatically. Read how to do this [here](/mephisto/generators/form_composer/README.md#using-formcomposerconfig-utility).
-Example of `form_composer_config` command will be:
+- Browser page is same as for the Simple form example
+
+There are variations of dynamic form config that use different providers. To try that, change `run_task_dynamic.py` in the launch command to:
+
+- `run_task_dynamic_ec2_prolific.py` for Prolific (requires valid EC2 credentials)
+- `run_task_dynamic_ec2_mturk_sandbox.py` for Mechanical Turk sandbox (requires valid EC2 credentials)
+
+##### 3.3. Dynamic form with presigned URLs
+
+This example builds further upon the Dynamic form example. Here we use presigned URLs (i.e. short-lived URLs for S3 AWS files) in the displayed forms, for data security.
+
+- Set up environment variables (in file `docker/envs/env.dev`):
+ - Required: valid AWS credentials: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_DEFAULT_REGION`
+ - Required: a private S3 bucket with files that you will embed in the example form (either replace dummy URLs in the configs by hand, or automatically generate new ones with `mephisto form_composer_config` command)
+ - Optional: `S3_URL_EXPIRATION_MINUTES` (default value is 60 minutes)
+- Default config file: [dynamic_example_local_mock.yaml](/examples/form_composer_demo/hydra_configs/conf/dynamic_example_local_mock.yaml).
+- Create config: see all options for `form_composer_config` command [here](/mephisto/generators/form_composer/README.md#using-formcomposerconfig-utility). Example command:
```shell
docker-compose -f docker/docker-compose.dev.yml run \
--build \
--publish 3001:3000 \
--rm mephisto_dc \
- mephisto form_composer_config --update-file-location-values "https://s3.amazon.com/...." --use_presigned_urls
+ mephisto form_composer_config --verify --directory "/mephisto/examples/form_composer_demo/data/dynamic_presigned_urls"
```
-After you generetad/changed all configs, you can launch with next command:
+- Launch command after you generated all configs:
```shell
docker-compose -f docker/docker-compose.dev.yml run \
--build \
@@ -117,6 +139,52 @@ After you generetad/changed all configs, you can launch with next command:
python /mephisto/examples/form_composer_demo/run_task_dynamic_presigned_urls.py
```
+
+# End-to-end example with AWS
+
+Putting it altogether, let's prepare and launch a task featuring a form containing one embedded file plus a few other fields. Here we'll assume working in directory `/mephisto/examples/form_composer_demo/data/dynamic_presigned_urls`.
+
+- Adjust `dynamic_presigned_urls_example_ec2_prolific.yaml` task config as needed
+- Create `form_config.json` file to determine your form fields and layout
+ - it should contain a token named `file_location`
+ - for more details see `mephisto/generators/form_composer/README.md`
+- Create `separate_token_values_config.json` with desired token values (except embedded files)
+- Remove content of folder `/tmp` (if you didn't shut the previous Task run correctly)
+- Specify your AWS credentials
+ - Create file `docker/aws_credentials` and populate it with AWS keys info
+ - Populate your AWS credentials into Docker env
+ - create local env `docker/envs/env.local` and add AWS keys into it (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_DEFAULT_REGION`)
+ - clone `docker/docker-compose.dev.yml` file as `docker/docker-compose.dev.yml`, and point it to the `env.local` environment
+- Stand up docker containers: `docker-compose -f docker/docker-compose.local.vscode.yml up`
+- SSH into the running container: `docker exec -it mephisto_dc bash`
+- Generate your task data config with these commands:
+ ```shell
+ mephisto form_composer_config \
+ --directory "/mephisto/examples/form_composer_demo/data/dynamic_presigned_urls" \
+ --update-file-location-values "https://dev-alented-private.s3.amazonaws.com/mephisto" \
+ --use-presigned-urls
+
+ mephisto form_composer_config \
+ --directory "/mephisto/examples/form_composer_demo/data/dynamic_presigned_urls" \
+ --permutate-separate-tokens
+
+ mephisto form_composer_config \
+ --directory "/mephisto/examples/form_composer_demo/data/dynamic_presigned_urls" \
+ --extrapolate-token-sets
+
+ mephisto form_composer_config \
+ --directory "/mephisto/examples/form_composer_demo/data/dynamic_presigned_urls" \
+ --verify
+ ```
+- Launch your task:
+ ```shell
+ cd /mephisto/examples/form_composer_demo && python run_task_dynamic_presigned_urls_ec2_prolific.py
+ ```
+- After the Task is completed by all workers, launch task review app (for more details see `mephisto/review_app/README.md`):
+ ```shell
+ mephisto review_app -h 0.0.0.0 -p 8000 -d True -f True
+ ```
+
---
# Your Mephisto project
diff --git a/examples/form_composer_demo/data/dynamic_presigned_urls/form_config.json b/examples/form_composer_demo/data/dynamic_presigned_urls/form_config.json
index 176bce404..73c628e66 100644
--- a/examples/form_composer_demo/data/dynamic_presigned_urls/form_config.json
+++ b/examples/form_composer_demo/data/dynamic_presigned_urls/form_config.json
@@ -292,7 +292,7 @@
"type": "input",
"validators": {
"required": true
- },
+ }
}
],
"help": "Please type in your favorite personal motto"
diff --git a/examples/form_composer_demo/data/dynamic_presigned_urls/separate_token_values_config.json b/examples/form_composer_demo/data/dynamic_presigned_urls/separate_token_values_config.json
index 6c425c1b3..802cc21a6 100644
--- a/examples/form_composer_demo/data/dynamic_presigned_urls/separate_token_values_config.json
+++ b/examples/form_composer_demo/data/dynamic_presigned_urls/separate_token_values_config.json
@@ -3,10 +3,10 @@
"Mephisto"
],
"file_location": [
- "{{getPresignedUrl(\"https://s3.amazonaws.com/path/to/your/folder/file1.jpg\")}}",
- "{{getPresignedUrl(\"https://s3.amazonaws.com/path/to/your/folder/file2.jpg\")}}"
+ "{{getMultiplePresignedUrls(\"https%3A//your-bucket.s3.amazonaws.com/your/folder/path/file1.jpg\")}}",
+ "{{getMultiplePresignedUrls(\"https%3A//your-bucket.s3.amazonaws.com/your/folder/path/file2.jpg\")}}"
],
"since_age": [
18
]
-}
+}
\ No newline at end of file
diff --git a/examples/form_composer_demo/data/dynamic_presigned_urls/task_data.json b/examples/form_composer_demo/data/dynamic_presigned_urls/task_data.json
index 9423d5b83..1e7658278 100644
--- a/examples/form_composer_demo/data/dynamic_presigned_urls/task_data.json
+++ b/examples/form_composer_demo/data/dynamic_presigned_urls/task_data.json
@@ -2,12 +2,12 @@
{
"form": {
"title": "Form example",
- "instruction": "Please answer all questions to the best of your ability as part of our study. Image .",
+ "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.",
"sections": [
{
"name": "section_about",
"title": "About you",
- "instruction": "Please introduce yourself. We would like to know more about your background, personal information, etc.",
+ "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
",
"collapsable": false,
"fieldsets": [
{
@@ -50,7 +50,7 @@
{
"fields": [
{
- "help": "We may contact you later for additional information",
+ "help": "We may contact you later at your Mephisto email for additional information",
"id": "id_email",
"label": "Email address for Mephisto",
"name": "email",
@@ -152,7 +152,7 @@
{
"help": "",
"id": "id_bio",
- "label": "Biography",
+ "label": "Biography since age of 18",
"name": "bio",
"placeholder": "",
"tooltip": "Your bio in a few paragraphs",
@@ -243,7 +243,7 @@
{
"fields": [
{
- "help": "",
+ "help": "We only accept files in PNG, JPG, and JPEG formats.",
"id": "id_avatar",
"label": "Profile Picture",
"name": "avatar",
@@ -251,7 +251,12 @@
"tooltip": "Your profile photo",
"type": "file",
"validators": {
- "required": true
+ "required": true,
+ "fileExtension": [
+ "png",
+ "jpg",
+ "jpeg"
+ ]
},
"value": ""
},
@@ -288,17 +293,14 @@
{
"fields": [
{
- "help": "",
"id": "id_motto",
"label": "Personal Motto",
"name": "motto",
- "placeholder": "",
"tooltip": "Your personal motto",
"type": "input",
"validators": {
"required": true
- },
- "value": ""
+ }
}
],
"help": "Please type in your favorite personal motto"
@@ -309,6 +311,7 @@
}
],
"submit_button": {
+ "instruction": "Please double-check if everything has been filled in correctly.",
"text": "Submit",
"tooltip": "Submit form"
}
@@ -317,12 +320,12 @@
{
"form": {
"title": "Form example",
- "instruction": "Please answer all questions to the best of your ability as part of our study. Image .",
+ "instruction": "Please answer all questions to the best of your ability as part of our study.
This is an example of how a completed task may look like:
.",
"sections": [
{
"name": "section_about",
"title": "About you",
- "instruction": "Please introduce yourself. We would like to know more about your background, personal information, etc.",
+ "instruction": "Please introduce yourself. We would like to know more about your:
- Background
- Personal information
- Etc
",
"collapsable": false,
"fieldsets": [
{
@@ -365,12 +368,12 @@
{
"fields": [
{
- "help": "We may contact you later for additional information",
+ "help": "We may contact you later at your Mephisto email for additional information",
"id": "id_email",
- "label": "Email address for Facebook",
+ "label": "Email address for Mephisto",
"name": "email",
"placeholder": "user@mephisto.ai",
- "tooltip": "Email address for Facebook",
+ "tooltip": "Email address for Mephisto",
"type": "email",
"validators": {
"required": true,
@@ -467,7 +470,7 @@
{
"help": "",
"id": "id_bio",
- "label": "Biography",
+ "label": "Biography since age of 18",
"name": "bio",
"placeholder": "",
"tooltip": "Your bio in a few paragraphs",
@@ -558,7 +561,7 @@
{
"fields": [
{
- "help": "",
+ "help": "We only accept files in PNG, JPG, and JPEG formats.",
"id": "id_avatar",
"label": "Profile Picture",
"name": "avatar",
@@ -566,7 +569,12 @@
"tooltip": "Your profile photo",
"type": "file",
"validators": {
- "required": true
+ "required": true,
+ "fileExtension": [
+ "png",
+ "jpg",
+ "jpeg"
+ ]
},
"value": ""
},
@@ -603,17 +611,14 @@
{
"fields": [
{
- "help": "",
"id": "id_motto",
"label": "Personal Motto",
"name": "motto",
- "placeholder": "",
"tooltip": "Your personal motto",
"type": "input",
"validators": {
"required": true
- },
- "value": ""
+ }
}
],
"help": "Please type in your favorite personal motto"
@@ -624,9 +629,10 @@
}
],
"submit_button": {
+ "instruction": "Please double-check if everything has been filled in correctly.",
"text": "Submit",
"tooltip": "Submit form"
}
}
}
-]
+]
\ No newline at end of file
diff --git a/examples/form_composer_demo/data/dynamic_presigned_urls/token_sets_values_config.json b/examples/form_composer_demo/data/dynamic_presigned_urls/token_sets_values_config.json
index 00e9e1df6..d2278804b 100644
--- a/examples/form_composer_demo/data/dynamic_presigned_urls/token_sets_values_config.json
+++ b/examples/form_composer_demo/data/dynamic_presigned_urls/token_sets_values_config.json
@@ -1,14 +1,16 @@
[
{
"tokens_values": {
- "file_location": "{{getPresignedUrl(\"https://s3.amazonaws.com/path/to/your/folder/file1.jpg\")}}",
+ "company_name": "Mephisto",
+ "file_location": "{{getMultiplePresignedUrls(\"https%3A//your-bucket.s3.amazonaws.com/your/folder/path/file1.jpg\")}}",
"since_age": 18
}
},
{
"tokens_values": {
- "file_location": "{{getPresignedUrl(\"https://s3.amazonaws.com/path/to/your/folder/file2.jpg\")}}",
+ "company_name": "Mephisto",
+ "file_location": "{{getMultiplePresignedUrls(\"https%3A//your-bucket.s3.amazonaws.com/your/folder/path/file2.jpg\")}}",
"since_age": 18
}
}
-]
+]
\ No newline at end of file
diff --git a/examples/form_composer_demo/hydra_configs/conf/dynamic_presigned_urls_example_ec2_prolific.yaml b/examples/form_composer_demo/hydra_configs/conf/dynamic_presigned_urls_example_ec2_prolific.yaml
index 3d11ff802..25beb52e8 100644
--- a/examples/form_composer_demo/hydra_configs/conf/dynamic_presigned_urls_example_ec2_prolific.yaml
+++ b/examples/form_composer_demo/hydra_configs/conf/dynamic_presigned_urls_example_ec2_prolific.yaml
@@ -15,11 +15,11 @@ mephisto:
profile_name: mephisto-router-iam
subdomain: "0123"
blueprint:
- task_source: ${task_dir}/webapp/build/bundle.js
+ task_source: ${task_dir}/webapp/build/bundle.presigned_urls.js
task_source_review: ${task_dir}/webapp/build/bundle.review.js
link_task_source: false
extra_source_dir: ${task_dir}/webapp/src/static
- units_per_assignment: 1
+ units_per_assignment: 2
log_level: "debug"
task:
task_name: "Form Builder Sample"
diff --git a/examples/form_composer_demo/preview/mturk_preview_template.html b/examples/form_composer_demo/preview/mturk_preview_template.html
index d02100c24..642b0427a 100644
--- a/examples/form_composer_demo/preview/mturk_preview_template.html
+++ b/examples/form_composer_demo/preview/mturk_preview_template.html
@@ -8,9 +8,9 @@
- {{title}}
+ [[ title ]]
- {{instruction}}
+ [[ instruction ]]
diff --git a/examples/form_composer_demo/run_task.py b/examples/form_composer_demo/run_task.py
index 0409b89db..080166f52 100644
--- a/examples/form_composer_demo/run_task.py
+++ b/examples/form_composer_demo/run_task.py
@@ -56,7 +56,7 @@ def _build_custom_bundles(cfg: DictConfig) -> None:
build_command="build:review",
)
- # Build UI for the application
+ # Build Task UI for the application
build_custom_bundle(
cfg.task_dir,
force_rebuild=cfg.mephisto.task.force_rebuild,
diff --git a/examples/form_composer_demo/run_task_dynamic.py b/examples/form_composer_demo/run_task_dynamic.py
index a12e8ed49..eb6ba5c33 100644
--- a/examples/form_composer_demo/run_task_dynamic.py
+++ b/examples/form_composer_demo/run_task_dynamic.py
@@ -62,7 +62,7 @@ def _build_custom_bundles(cfg: DictConfig) -> None:
build_command="build:review",
)
- # Build UI for the application
+ # Build Task UI for the application
build_custom_bundle(
cfg.task_dir,
force_rebuild=cfg.mephisto.task.force_rebuild,
diff --git a/examples/form_composer_demo/run_task_dynamic_ec2_mturk_sandbox.py b/examples/form_composer_demo/run_task_dynamic_ec2_mturk_sandbox.py
index b2dedf3aa..cd58d67fb 100644
--- a/examples/form_composer_demo/run_task_dynamic_ec2_mturk_sandbox.py
+++ b/examples/form_composer_demo/run_task_dynamic_ec2_mturk_sandbox.py
@@ -82,7 +82,7 @@ def _build_custom_bundles(cfg: DictConfig) -> None:
build_command="build:review",
)
- # Build UI for the application
+ # Build Task UI for the application
build_custom_bundle(
cfg.task_dir,
force_rebuild=cfg.mephisto.task.force_rebuild,
@@ -112,6 +112,9 @@ def generate_data_json_config():
def generate_preview_html():
+ """
+ Generate HTML preview of a Task (based on first form version contained in `task_data.json`)
+ """
app_path = os.path.dirname(os.path.abspath(__file__))
preview_path = os.path.join(app_path, "preview")
data_path = os.path.join(app_path, "data", "dynamic")
@@ -127,11 +130,15 @@ def generate_preview_html():
print(f"Could not read JSON from '{data_config_path}' file")
raise
- first_form_data = data_config_data[0]
+ first_form_version = data_config_data[0]["form"]
+ # Erase all tokens from the text since HTML preview is inherently static
+ erase_tokens = lambda text: re.sub(
+ TOKEN_START_REGEX + r"(.*?)" + TOKEN_END_REGEX, ".....", text,
+ )
preview_data = {
- "title": first_form_data["form"]["title"],
- "instruction": first_form_data["form"]["instruction"],
+ "title": erase_tokens(first_form_version["title"]),
+ "instruction": erase_tokens(first_form_version["instruction"]),
}
with open(preview_template_path, "r") as f:
@@ -139,8 +146,9 @@ def generate_preview_html():
with open(preview_html_path, "w") as f:
for attr_name, value in preview_data.items():
+ # Simply replace `[[ token_name ]]` substrings in the HTML template for our valoues
preview_template = re.sub(
- TOKEN_START_REGEX + r"(\s*)" + attr_name + r"(\s*)" + TOKEN_END_REGEX,
+ r"\[\[(\s*)" + attr_name + r"(\s*)\]\]",
value,
preview_template,
)
diff --git a/examples/form_composer_demo/run_task_dynamic_ec2_prolific.py b/examples/form_composer_demo/run_task_dynamic_ec2_prolific.py
index 950c88393..54f1b9221 100644
--- a/examples/form_composer_demo/run_task_dynamic_ec2_prolific.py
+++ b/examples/form_composer_demo/run_task_dynamic_ec2_prolific.py
@@ -84,7 +84,7 @@ def _build_custom_bundles(cfg: DictConfig) -> None:
build_command="build:review",
)
- # Build UI for the application
+ # Build Task UI for the application
build_custom_bundle(
cfg.task_dir,
force_rebuild=cfg.mephisto.task.force_rebuild,
diff --git a/examples/form_composer_demo/run_task_dynamic_presigned_urls.py b/examples/form_composer_demo/run_task_dynamic_presigned_urls_ec2_prolific.py
similarity index 94%
rename from examples/form_composer_demo/run_task_dynamic_presigned_urls.py
rename to examples/form_composer_demo/run_task_dynamic_presigned_urls_ec2_prolific.py
index 8c12b033f..9d1496d12 100644
--- a/examples/form_composer_demo/run_task_dynamic_presigned_urls.py
+++ b/examples/form_composer_demo/run_task_dynamic_presigned_urls_ec2_prolific.py
@@ -23,7 +23,7 @@
from mephisto.tools.scripts import task_script
-@task_script(default_config_file="dynamic_example_local_mock")
+@task_script(default_config_file="dynamic_presigned_urls_example_ec2_prolific")
def main(operator: Operator, cfg: DictConfig) -> None:
# Build packages
_build_custom_bundles(cfg)
@@ -32,7 +32,7 @@ def main(operator: Operator, cfg: DictConfig) -> None:
task_data_config_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"data",
- "dynamic",
+ "dynamic_presigned_urls",
"task_data.json",
)
task_data = read_config_file(task_data_config_path)
@@ -79,11 +79,12 @@ def _build_custom_bundles(cfg: DictConfig) -> None:
build_command="build:review",
)
- # Build UI for the application
+ # Build Task UI for the application
build_custom_bundle(
cfg.task_dir,
force_rebuild=cfg.mephisto.task.force_rebuild,
post_install_script=cfg.mephisto.task.post_install_script,
+ build_command="build:presigned_urls",
)
diff --git a/examples/form_composer_demo/webapp/package.json b/examples/form_composer_demo/webapp/package.json
index 9920e9dea..7a9ab4da8 100644
--- a/examples/form_composer_demo/webapp/package.json
+++ b/examples/form_composer_demo/webapp/package.json
@@ -7,7 +7,8 @@
"dev": "webpack --mode development",
"dev:watch": "webpack --mode development --watch",
"test": "cypress open",
- "build:review": "webpack --config=webpack.config.review.js --mode development"
+ "build:review": "webpack --config=webpack.config.review.js --mode development",
+ "build:presigned_urls": "webpack --config=webpack.config.presigned_urls.js --mode development"
},
"keywords": [],
"author": "",
diff --git a/examples/form_composer_demo/webapp/src/presigned_urls.js b/examples/form_composer_demo/webapp/src/presigned_urls.js
new file mode 100644
index 000000000..be3a4eedc
--- /dev/null
+++ b/examples/form_composer_demo/webapp/src/presigned_urls.js
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) Meta Platforms and its affiliates.
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import "bootstrap/dist/css/bootstrap.css";
+import "bootstrap-select/dist/css/bootstrap-select.min.css";
+import "./presigned_urls_app.jsx";
+import "./css/style.css";
diff --git a/examples/form_composer_demo/webapp/src/presign_urls_app.jsx b/examples/form_composer_demo/webapp/src/presigned_urls_app.jsx
similarity index 96%
rename from examples/form_composer_demo/webapp/src/presign_urls_app.jsx
rename to examples/form_composer_demo/webapp/src/presigned_urls_app.jsx
index f11f73f32..940a95bcf 100644
--- a/examples/form_composer_demo/webapp/src/presign_urls_app.jsx
+++ b/examples/form_composer_demo/webapp/src/presigned_urls_app.jsx
@@ -6,7 +6,7 @@
import React from "react";
import ReactDOM from "react-dom";
-import { FormComposerBaseFrontend, LoadingScreen } from "./components/core_components.jsx";
+import { FormComposerBaseFrontend, LoadingScreen } from "./components/core_components_presigned_url.jsx";
import { useMephistoRemoteProcedureTask, ErrorBoundary } from "mephisto-task-multipart";
/* ================= Application Components ================= */
diff --git a/examples/form_composer_demo/webapp/webpack.config.presign_urls.js b/examples/form_composer_demo/webapp/webpack.config.presigned_urls.js
similarity index 84%
rename from examples/form_composer_demo/webapp/webpack.config.presign_urls.js
rename to examples/form_composer_demo/webapp/webpack.config.presigned_urls.js
index 45902b48d..4ca86cd42 100644
--- a/examples/form_composer_demo/webapp/webpack.config.presign_urls.js
+++ b/examples/form_composer_demo/webapp/webpack.config.presigned_urls.js
@@ -8,15 +8,18 @@ var path = require("path");
var webpack = require("webpack");
module.exports = {
- entry: "./src/presign_urls_app.js",
+ entry: "./src/presigned_urls.js",
output: {
path: __dirname,
- filename: "build/bundle.presign_urls.js",
+ filename: "build/bundle.presigned_urls.js",
},
resolve: {
alias: {
react: path.resolve("./node_modules/react"),
// Use local library with code that can submit FormData
+ "mephisto-task-multipart": path.resolve(
+ __dirname, "../../../packages/mephisto-task-multipart",
+ ),
"react-form-composer": path.resolve(
__dirname, "../../../packages/react-form-composer",
),
diff --git a/mephisto/client/cli.py b/mephisto/client/cli.py
index 5e4df164b..0d575050d 100644
--- a/mephisto/client/cli.py
+++ b/mephisto/client/cli.py
@@ -535,7 +535,7 @@ def form_composer_config(
# Run the command
if verify:
- print(f"[green]Started configs verification in '{task_data_config_path}'[/green]")
+ print(f"Started configs verification")
verify_form_composer_configs(
task_data_config_path=task_data_config_path,
form_config_path=form_config_path,
@@ -543,7 +543,7 @@ def form_composer_config(
separate_token_values_config_path=separate_token_values_config_path,
task_data_config_only=False,
)
- print(f"[green]Finished successfully[/green]")
+ print(f"Finished configs verification")
elif update_file_location_values:
print(
diff --git a/mephisto/generators/form_composer/README.md b/mephisto/generators/form_composer/README.md
index d4399088e..faf7949d8 100644
--- a/mephisto/generators/form_composer/README.md
+++ b/mephisto/generators/form_composer/README.md
@@ -65,27 +65,26 @@ The `form_composer_config` utility command helps auto-generate FormComposer conf
```shell
# Sample launching commands
-mephisto form_composer_config --verify
-mephisto form_composer_config --extrapolate-token-sets
+mephisto form_composer_config --update-file-location-values "https://s3.amazonaws.com/..." --use_presigned_urls
+mephisto form_composer_config --update-file-location-values "https://s3.amazonaws.com/..."
mephisto form_composer_config --permutate-separate-tokens
-mephisto form_composer_config --update-file-location-values "https://s3.amazon.com/...."
+mephisto form_composer_config --extrapolate-token-sets
+mephisto form_composer_config --verify
# Parameters that work together
mephisto form_composer_config --directory /my/own/path/to/data/ --verify
mephisto form_composer_config --directory /my/own/path/to/data/ --extrapolate-token-sets
-mephisto form_composer_config --update-file-location-values "https://s3.amazon.com/...." --use_presigned_urls
+mephisto form_composer_config --update-file-location-values "https://s3.amazonaws.com/..."
```
where
+- `-d/--directory` - a **modifier** for all `form_composer_config` command options that specifies the directory where all form JSON config files are located (if missing the default is `mephisto/generators/form_composer/data` directory)
- `-v/--verify` - if truthy, validates all JSON configs currently present in the form builder config directory
-- `-f/--update-file-location-values S3_URL` - generates token values based on file names found within specified S3 folder (see a separate section about this mode of running FormComposer)
-- `-e/--extrapolate-token-sets` - if truthy, generates Task data config based on provided form config and takon sets values
- `-p/--permutate-sepatate-tokens` - if truthy, generates token sets values as all possible combinations of values of individual tokens
-- `-d/--directory` - sets directory where parameters above find configs to work (omittig sets default directory which is `form_composer` generator's data directory)
-- `-u/--use_presigned_urls` - works together with `--update-file-location-values` and tells that all URLs from S3 will be wrapped with special token `{{getPresignedUrl(
)}}`.
- When worker opens a task page this token will be overridden with S3 presigned URL. This kind of URLs is available for some period of time (default 1 hour).
- If you want to change this period set environment variable `S3_URL_EXPIRATION_MINUTES`
+- `-f/--update-file-location-values S3_FOLDER_URL` - generates token values based on file names found within the specified S3 folder (see a separate section about this mode of running FormComposer)
+- `-e/--extrapolate-token-sets` - if truthy, generates Task data config based on provided form config and takon sets values
+- `-u/--use-presigned-urls` - a **modifier** for `--update-file-location-values` command that converts S3 URLs into short-lived rtemporary ones (for more detailes see "Presigned URLs" section)
-To understand what "tokens" means, read on about FormComposer config structure.
+To understand what the concept of "tokens" means, read on about FormComposer config structure.
## Config files
@@ -203,6 +202,16 @@ In a special case when one of your tokens is an S3 file URL, that token values c
---
+#### Mturk Task Preview
+
+For Tasks run with Mechanical Turk provider, FormComposer generates a Task preview (a small HTML snippet shown to worker prior to starting the task). This Task review comprises HTML content of `form` object's attributes `title` and `instruction`.
+
+However, note that the task preview is inherently static, therefore:
+- we always take the first form version in `data_task.json` to generate Task preview for all form versions
+- we erase dynamic tokens from the Task review content
+
+---
+
## Dynamic form config example
Putting it altogether, this is a brief example of composing a dynamic form config.
@@ -239,17 +248,17 @@ Permutating these token values will produce this `form_config.json` file with to
]
```
-Example of config after using `--update-file-location-values "https://s3.amazon.com/...." --use_presigned_urls` params:
+Example of config after using `--update-file-location-values "https://s3.amazonaws.com/...." --use_presigned_urls` params:
```json
[
{
"tokens_values": {
- "file_location": "{{getPresignedUrl(\"https://s3.amazon.com/1.jpg\")}}"
+ "file_location": "{{getPresignedUrl(\"https://s3.amazonaws.com/1.jpg\")}}"
}
},
{
"tokens_values": {
- "file_location": "{{getPresignedUrl(\"https://s3.amazon.com/2.jpg\")}}"
+ "file_location": "{{getPresignedUrl(\"https://s3.amazonaws.com/2.jpg\")}}"
}
},
]
@@ -321,10 +330,37 @@ TBD
---
-# Custom callbacks
+# Form callbacks
+
+During rendering of a Task in the browser, we may send calls to the server-side for additional data. In Mephisto, API views servicing such requests are called "remote procedures".
+
+
+## Presigned S3 URLs
+
+An example of a remote procedure that gets called during the initial form loading, is `getPresignedUrl` and `getMultiplePresignedUrls` functions. These functions allow to generate short-lived S3 URLs, in order to limit exposure of sensitive resources.
+
+The below command auto-generates config token values that presign themselves during rendering of the Task page:
+
+```
+mephisto form_composer_config --update-file-location-values "https://s3.amazonaws.com/..." --use_presigned_urls
+```
+
+This is how URL pre-signing works:
+ - When a worker opens the Task page and the form HTML is generated, it will contain so-called "procedure tokens", i.e. token values that look like this: `{{getMultiplePresignedUrls()}}`
+ - the "wrapper" part of a procedure token is the name of a Javascript function that will render itself dynamically (e.g. by calling some remote API to receive additional data)
+ - the argument part is the argument value provided suring the function call
+ - As soon as the form HTML is in place, the remote procedure gets called
+ - Mephisto's predefined remote procedure generates presigned URL, and its expiration starts ticking
+
+Presigned S3 URLs use the following environment variables:
+ - Required: valid AWS credentials: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_DEFAULT_REGION`
+ form_composer_config` command)
+ - Optional: URL expiration time `S3_URL_EXPIRATION_MINUTES`, can be up to 7 days long (if missing the default value is 60 minutes)
+
-TBD (aka "remote procedure")
+## Custom callbacks
+You can write your own remote procedures. A good place to start is looking at how S3 URL presigning is implemented in the `examples/form_composer_demo` example project.
-----
diff --git a/mephisto/generators/form_composer/config_validation/separate_token_values_config.py b/mephisto/generators/form_composer/config_validation/separate_token_values_config.py
index fadbffc2a..227543ac7 100644
--- a/mephisto/generators/form_composer/config_validation/separate_token_values_config.py
+++ b/mephisto/generators/form_composer/config_validation/separate_token_values_config.py
@@ -3,7 +3,7 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
-import urllib.parse
+import os
from typing import Dict
from typing import List
from typing import Tuple
@@ -17,6 +17,7 @@
from mephisto.generators.form_composer.remote_procedures import ProcedureName
from .config_validation_constants import FILE_URL_TOKEN_KEY
from .utils import get_file_urls_from_s3_storage
+from .utils import read_config_file
from .utils import write_config_to_file
@@ -66,13 +67,19 @@ def update_separate_token_values_config_with_file_urls(
files_locations = [
(
TOKEN_START_SYMBOLS +
- f"{ProcedureName.GET_MULTIPLE_PRESIGNED_URLS}({urllib.parse.quote(url)})" +
+ f'{ProcedureName.GET_MULTIPLE_PRESIGNED_URLS}("{url}")' +
TOKEN_END_SYMBOLS
)
for url in files_locations
]
- separate_token_values_config_data = {
+ # Update data in existing config file, or create new if it doesn't exist
+ config_data = {}
+ if os.path.exists(separate_token_values_config_path):
+ config_data = read_config_file(separate_token_values_config_path)
+
+ config_data.update({
FILE_URL_TOKEN_KEY: files_locations,
- }
- write_config_to_file(separate_token_values_config_data, separate_token_values_config_path)
+ })
+
+ write_config_to_file(config_data, separate_token_values_config_path)
diff --git a/mephisto/generators/form_composer/config_validation/task_data_config.py b/mephisto/generators/form_composer/config_validation/task_data_config.py
index d0eea31e4..cfd92d598 100644
--- a/mephisto/generators/form_composer/config_validation/task_data_config.py
+++ b/mephisto/generators/form_composer/config_validation/task_data_config.py
@@ -6,6 +6,7 @@
import os.path
import re
from copy import deepcopy
+from rich import print
from typing import List
from typing import Optional
from typing import Tuple
@@ -28,7 +29,7 @@ def _extrapolate_tokens_values(text: str, tokens_values: dict) -> str:
for token, value in tokens_values.items():
text = re.sub(
TOKEN_START_REGEX + r"(\s*)" + token + r"(\s*)" + TOKEN_END_REGEX,
- value,
+ str(value),
text,
)
return text
@@ -120,7 +121,6 @@ def _validate_tokens_in_both_configs(
token_name
for token_set_values_data in token_sets_values_config_data
for token_name in token_set_values_data.get(TOKENS_VALUES_KEY, {}).keys()
-
])
# Token names present in token values config, but not in form config
overspecified_tokens = tokens_from_token_sets_values_config - tokens_from_form_config
@@ -173,11 +173,11 @@ def _combine_extrapolated_form_configs(
errors = errors + tokens_in_unexpected_attrs_errors
if not form_config_is_valid:
- errors.append(make_error_message("Form config is invalid.", form_config_errors))
+ errors.append(make_error_message("Form config is invalid", form_config_errors))
if not token_sets_values_config_is_valid:
errors.append(make_error_message(
- "Toekn sets values config is invalid.", token_sets_values_data_config_errors,
+ "Token sets values config is invalid", token_sets_values_data_config_errors,
))
if errors:
@@ -228,7 +228,8 @@ def create_extrapolated_config(
)
write_config_to_file(extrapolated_form_config_data, task_data_config_path)
except ValueError as e:
- print(f"Could not extrapolate form configs: {e}")
+ print(f"\n[red]Could not extrapolate form configs:[/red] {e}\n")
+ exit()
def validate_task_data_config(config_json: List[dict]) -> Tuple[bool, List[str]]:
@@ -264,47 +265,64 @@ def verify_form_composer_configs(
errors = []
try:
- # 1. Validate data config
- task_data_config_data = read_config_file(task_data_config_path)
-
- task_data_config_is_valid, task_data_config_errors = validate_task_data_config(
- task_data_config_data,
- )
+ # 1. Validate task data config
+ task_data_config_data = read_config_file(task_data_config_path, exit_if_no_file=False)
- if not task_data_config_is_valid:
- errors.append(make_error_message(
- "Task data config is invalid.", task_data_config_errors,
- ))
+ if task_data_config_data is None:
+ pass
+ else:
+ task_data_config_is_valid, task_data_config_errors = validate_task_data_config(
+ task_data_config_data,
+ )
+ if not task_data_config_is_valid:
+ errors.append(make_error_message(
+ "Task data config is invalid", task_data_config_errors,
+ ))
if task_data_config_only:
if errors:
- raise ValueError("\n" + "\n\n".join(errors))
+ raise ValueError(make_error_message("", errors))
return None
- # 2. Validate form config config
- form_config_data = read_config_file(form_config_path)
+ # 2. Validate form config
+ form_config_data = read_config_file(form_config_path, exit_if_no_file=False)
- form_config_is_valid, form_config_errors = validate_form_config(form_config_data)
+ if form_config_data is None:
+ pass
+ else:
+ form_config_is_valid, form_config_errors = validate_form_config(form_config_data)
- if not form_config_is_valid:
- errors.append(make_error_message("Form config is invalid.", form_config_errors))
+ if not form_config_is_valid:
+ errors.append(make_error_message("Form config is invalid", form_config_errors))
# 3. Validate token sets values config
- if os.path.exists(token_sets_values_config_path):
- token_sets_values_data = read_config_file(token_sets_values_config_path)
- else:
+ token_sets_values_data = read_config_file(
+ token_sets_values_config_path, exit_if_no_file=False,
+ )
+
+ # 3a. Validate token sets values data itself
+ if token_sets_values_data is None:
token_sets_values_data = []
+ else:
+ token_sets_values_config_is_valid, token_sets_values_config_errors = (
+ validate_token_sets_values_config(token_sets_values_data)
+ )
+
+ if not token_sets_values_config_is_valid:
+ errors.append(make_error_message(
+ "Token sets values config is invalid",
+ token_sets_values_config_errors,
+ indent=4,
+ ))
+ # 3b. Ensure there's no unused token names in both token setes and form config
(
overspecified_tokens,
underspecified_tokens,
tokens_in_unexpected_attrs_errors,
- ) = _validate_tokens_in_both_configs(
- form_config_data, token_sets_values_data,
- )
+ ) = _validate_tokens_in_both_configs(form_config_data, token_sets_values_data)
- # Output errors, if any
if overspecified_tokens:
errors.append(
f"Values for the following tokens are provided in token sets values config, "
@@ -322,21 +340,23 @@ def verify_form_composer_configs(
errors = errors + tokens_in_unexpected_attrs_errors
# 4. Validate separate token values config
- separate_token_values_config_data = read_config_file(separate_token_values_config_path)
+ separate_token_values_config_data = read_config_file(
+ separate_token_values_config_path, exit_if_no_file=False,
+ )
separate_token_values_config_is_valid, separate_token_values_config_errors = (
validate_separate_token_values_config(separate_token_values_config_data)
)
if not separate_token_values_config_is_valid:
- token_sets_values_data_config_errors = [
- f" - {e}" for e in separate_token_values_config_errors
- ]
- errors_string = "\n".join(token_sets_values_data_config_errors)
- errors.append(f"Single token values config is invalid. Errors:\n{errors_string}")
+ errors.append(make_error_message(
+ "Separate token values config is invalid", separate_token_values_config_errors,
+ ))
if errors:
- raise ValueError("\n" + "\n\n".join(errors))
+ raise ValueError(make_error_message("", errors))
+ else:
+ print(f"[green]All configs are valid.[/green]")
except ValueError as e:
- print(f"Could not extrapolate form configs: {e}")
+ print(f"\n[red]Provided Form Composer config files are invalid:[/red] {e}\n")
diff --git a/mephisto/generators/form_composer/config_validation/token_sets_values_config.py b/mephisto/generators/form_composer/config_validation/token_sets_values_config.py
index 6e946e02f..286c6fe7c 100644
--- a/mephisto/generators/form_composer/config_validation/token_sets_values_config.py
+++ b/mephisto/generators/form_composer/config_validation/token_sets_values_config.py
@@ -25,26 +25,36 @@
]
-def validate_token_sets_values_config(config_json: List[dict]) -> Tuple[bool, List[str]]:
+def validate_token_sets_values_config(config_data: List[dict]) -> Tuple[bool, List[str]]:
is_valid = True
errors = []
- if not isinstance(config_json, list):
+ if not isinstance(config_data, list):
is_valid = False
errors.append("Config must be a JSON Array.")
- if config_json:
- if not all(config_json):
+ if config_data:
+ if not all(config_data):
is_valid = False
errors.append("Config must contain at least one non-empty item.")
- for item in config_json:
+ # Ensure each token set has correct JSON structure
+ for item in config_data:
item_is_valid = validate_config_dict_item(
item, "item_tokens_values", AVAILABLE_TASK_ATTRS, errors,
)
if not item_is_valid:
is_valid = False
+ # Ensure all token sets have the same set of keys (i.e. token names)
+ token_names_from_token_sets_values_config = [
+ tuple(sorted(token_set_values_data.get(TOKENS_VALUES_KEY, {}).keys()))
+ for token_set_values_data in config_data
+ ]
+ if len(set(token_names_from_token_sets_values_config)) > 1:
+ is_valid = False
+ errors.append("Some token sets contain dissimilar set of token names.")
+
return is_valid, errors
@@ -85,7 +95,7 @@ def update_token_sets_values_config_with_premutated_data(
errors = []
if not separate_token_values_config_is_valid:
errors.append(make_error_message(
- "Separate token values config is invalid.", separate_token_values_config_errors,
+ "Separate token values config is invalid", separate_token_values_config_errors,
))
if errors:
diff --git a/mephisto/generators/form_composer/config_validation/utils.py b/mephisto/generators/form_composer/config_validation/utils.py
index b18458742..edc16d090 100644
--- a/mephisto/generators/form_composer/config_validation/utils.py
+++ b/mephisto/generators/form_composer/config_validation/utils.py
@@ -35,19 +35,33 @@ def write_config_to_file(config_data: Union[List[dict], dict], file_path: str):
f.write(config_str)
-def read_config_file(config_path: str) -> Union[List[dict], dict]:
+def read_config_file(
+ config_path: str, exit_if_no_file: bool = True
+) -> Union[List[dict], dict, None]:
+
+ if not os.path.exists(config_path):
+ if exit_if_no_file:
+ print(f"[red]Required file not found: '{config_path}'.[/red]")
+ exit()
+
+ print(f"[yellow]Required file not found: '{config_path}'.[/yellow]")
+ return None
+
try:
with open(config_path) as config_file:
config_data = json.load(config_file)
- except (JSONDecodeError, TypeError, FileNotFoundError):
- print(f"[red]Could not read JSON from '{config_path}' file[/red]")
+ except (JSONDecodeError, TypeError):
+ print(f"[red]Could not read JSON from file: '{config_path}'.[/red]")
exit()
+
return config_data
-def make_error_message(main_message: str, error_list: List[str]) -> str:
- errors_bullet = "\n - " + "\n - ".join(map(str, error_list))
- return f"{main_message}. Errors:{errors_bullet}"
+def make_error_message(main_message: str, error_list: List[str], indent: int = 2) -> str:
+ prefix = "\n" + (" " * indent) + "- "
+ errors_bullets = prefix + prefix.join(map(str, error_list))
+ error_title = f"{main_message.rstrip('.')}. Errors:" if main_message else ""
+ return error_title + errors_bullets
def get_file_ext(file_name: str) -> str:
diff --git a/mephisto/generators/form_composer/run.py b/mephisto/generators/form_composer/run.py
index 335adfeb3..c27466325 100644
--- a/mephisto/generators/form_composer/run.py
+++ b/mephisto/generators/form_composer/run.py
@@ -74,7 +74,7 @@ def _build_custom_bundles(cfg: DictConfig) -> None:
build_command="build:review",
)
- # Build UI for the application
+ # Build Task UI for the application
build_custom_bundle(
cfg.task_dir,
force_rebuild=cfg.mephisto.task.force_rebuild,
diff --git a/packages/react-form-composer/src/FormComposer/utils.js b/packages/react-form-composer/src/FormComposer/utils.js
index f1a0121d7..af4700cc8 100644
--- a/packages/react-form-composer/src/FormComposer/utils.js
+++ b/packages/react-form-composer/src/FormComposer/utils.js
@@ -18,12 +18,12 @@ const WAIT_FOR_AGENT_ID_MSEC = 1000;
let tokenProcedureResultMapping = {};
-const procedureRegex = /([\w\(\)\"\'\/\.\_\-\:\{\}\s\\]+?)/;
const optionalSpacesRegex = /\s*/;
const openingRegex = new RegExp(TOKEN_START_REGEX.source + optionalSpacesRegex.source, "gi");
const closingRegex = new RegExp(optionalSpacesRegex.source + TOKEN_END_REGEX.source, "gi");
+const innerRegex = /(.*?)/;
export const procedureTokenRegex = new RegExp(
- openingRegex.source + procedureRegex.source + closingRegex.source,
+ openingRegex.source + innerRegex.source + closingRegex.source,
"gi",
);
@@ -95,9 +95,9 @@ function _getUrlsFromString(string) {
const matches = [...string.matchAll(procedureTokenRegex)];
matches.forEach(([token, procedureCode]) => {
if (procedureCode.includes(ProcedureName.GET_MULTIPLE_PRESIGNED_URLS)) {
- const procedureCodeMatches = [...procedureCode.matchAll(/\(\"([^\s]+)\"\)/gi)];
- if (procedureCodeMatches.length && procedureCodeMatches[0].length === 2) {
- const procedureCodeUrl = procedureCodeMatches[0][1];
+ const procedureCodeWithUrlMatches = [...procedureCode.matchAll(/\(\"(.+?)\"\)/gi)];
+ if (procedureCodeWithUrlMatches.length && procedureCodeWithUrlMatches[0].length === 2) {
+ const procedureCodeUrl = procedureCodeWithUrlMatches[0][1];
urls.push(procedureCodeUrl);
urlTotokenProcedureMapping[procedureCodeUrl] = token;
}