diff --git a/examples/simple_form_composer/README.md b/examples/simple_form_composer/README.md index 58e003ab3..a3da5ee96 100644 --- a/examples/simple_form_composer/README.md +++ b/examples/simple_form_composer/README.md @@ -1,6 +1,4 @@ -# Form Composer - -This is a Task generator that provides Tasks for simple questionnaire projects. +This form-based questionnaire is a simple example of Form Composer task generator. --- @@ -8,44 +6,31 @@ This is a Task generator that provides Tasks for simple questionnaire projects. 1. In repo root, launch containers: `docker-compose -f docker/docker-compose.dev.yml up` 2. SSH into running container to run server: `docker exec -it mephisto_dc bash` -3. Inside the container run server: +3. Inside the container run server: 1. Build `react-form-composer` package `cd /mephisto/packages/react-form-composer && npm run build` - 2. Run project: `cd /mephisto/examples/simple_form_composer && python ./run_task.py` + 2. Run project with + - simple data config: `cd /mephisto/examples/simple_form_composer && python ./run_task.py` + - dynamic data configs: `cd /mephisto/examples/simple_form_composer && python ./run_task_dynamic.py` --- ### How to configure -All you need to do is provide Form Builder with a JSON configuration of your form fields. +1. For simple data config you need to provide Form Composer with one JSON file - a configuration of your form fields. +An example is found in `examples/simple_form_composer/data/simple/data.json` file. +2. For dynamic data configs you need two JSON files: + - form configuration `examples/simple_form_composer/data/dynamic/form_config.json` + - tokens values `examples/simple_form_composer/data/dynamic/tokens_values_config.json` -An example is found in `examples/simple_form_composer/data/data.json` file. +Note that during bulding a Task with dynamic form config, the resulting data config will be placed in `data.json` file, i.e. `examples/simple_form_composer/data/dynamic/data.json` (in this example it's already been created and will be overwritten when you build a Task). --- -### How form is composed - -Form Builder supports several layers of hierarchy: - -1. Section -2. Fieldset -3. Fields Row -4. Field - ---- - -### Validators - -Available validators: - - `required` (boolean) - - `minLength` (integer) - - `maxLength` (integer) - - `regexp` (string | array[string, string]) +### Form config -`regexp` params: -1. RedExp string (`"^[a-zA-Z0-9._-]+@mephisto\\.ai$"`). Default flags are `igm` -2. Array with RedExp string and flags (`["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]`) +For details on how form config is composed, and how data fields are validated please see the main Form Composer's README. -Example: +Here's a sample part of form config: ```json { @@ -61,7 +46,7 @@ Example: "required": true, "minLength": 2, "maxLength": 20, - "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"] + "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"] // or just string "regexp": "^[a-zA-Z0-9._-]+@mephisto\\.ai$" }, "value": "" diff --git a/examples/simple_form_composer/data/dynamic/data.json b/examples/simple_form_composer/data/dynamic/data.json new file mode 100644 index 000000000..11ff580bc --- /dev/null +++ b/examples/simple_form_composer/data/dynamic/data.json @@ -0,0 +1,628 @@ +[ + { + "form": { + "title": "Form example", + "instruction": "Please answer all questions to the best of your ability as part of our study.", + "sections": [ + { + "name": "section_about", + "title": "About you", + "instruction": "Please introduce yourself. We would like to know more about your background, personal information, etc.", + "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": "Optional", + "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 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", + "name": "bio", + "placeholder": "", + "tooltip": "Your bio in a few apragraphs", + "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": "", + "id": "id_avatar", + "label": "Profile Picture", + "name": "avatar", + "placeholder": "Select a file", + "tooltip": "Your profile photo", + "type": "file", + "validators": { + "required": true + }, + "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", + "fieldsets": [ + { + "title": "Motivation", + "instruction": "", + "rows": [ + { + "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" + } + ] + } + ] + } + ], + "submit_button": { + "text": "Submit", + "tooltip": "Submit form" + } + } + }, + { + "form": { + "title": "Form example", + "instruction": "Please answer all questions to the best of your ability as part of our study.", + "sections": [ + { + "name": "section_about", + "title": "About you", + "instruction": "Please introduce yourself. We would like to know more about your background, personal information, etc.", + "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": "Optional", + "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 for additional information", + "id": "id_email", + "label": "Email address for Facebook", + "name": "email", + "placeholder": "user@mephisto.ai", + "tooltip": "Email address for Facebook", + "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", + "name": "bio", + "placeholder": "", + "tooltip": "Your bio in a few apragraphs", + "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": "", + "id": "id_avatar", + "label": "Profile Picture", + "name": "avatar", + "placeholder": "Select a file", + "tooltip": "Your profile photo", + "type": "file", + "validators": { + "required": true + }, + "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", + "fieldsets": [ + { + "title": "Motivation", + "instruction": "", + "rows": [ + { + "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" + } + ] + } + ] + } + ], + "submit_button": { + "text": "Submit", + "tooltip": "Submit form" + } + } + } +] \ No newline at end of file diff --git a/examples/simple_form_composer/data/dynamic/form_config.json b/examples/simple_form_composer/data/dynamic/form_config.json new file mode 100644 index 000000000..07f22d05b --- /dev/null +++ b/examples/simple_form_composer/data/dynamic/form_config.json @@ -0,0 +1,296 @@ +{ + "form": { + "title": "Form example", + "instruction": "Please answer all questions to the best of your ability as part of our study.", + "sections": [ + { + "name": "section_about", + "title": "About you", + "instruction": "Please introduce yourself. We would like to know more about your background, personal information, etc.", + "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": "Optional", + "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 for additional information", + "id": "id_email", + "label": "Email address for {{company_name}}", + "name": "email", + "placeholder": "user@mephisto.ai", + "tooltip": "Email address for {{company_name}}", + "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", + "name": "bio", + "placeholder": "", + "tooltip": "Your bio in a few apragraphs", + "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": "", + "id": "id_avatar", + "label": "Profile Picture", + "name": "avatar", + "placeholder": "Select a file", + "tooltip": "Your profile photo", + "type": "file", + "validators": {"required": true}, + "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", + "fieldsets" : [ + { + "title": "Motivation", + "instruction": "", + "rows": [ + { + "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" + } + ] + } + ] + } + ], + "submit_button": { + "text": "Submit", + "tooltip": "Submit form" + } + } +} diff --git a/examples/simple_form_composer/data/dynamic/tokens_values_config.json b/examples/simple_form_composer/data/dynamic/tokens_values_config.json new file mode 100644 index 000000000..5f7ab4e96 --- /dev/null +++ b/examples/simple_form_composer/data/dynamic/tokens_values_config.json @@ -0,0 +1,12 @@ +[ + { + "tokens_values": { + "company_name": "Mephisto" + } + }, + { + "tokens_values": { + "company_name": "Facebook" + } + } +] diff --git a/examples/simple_form_composer/data/data.json b/examples/simple_form_composer/data/simple/data.json similarity index 100% rename from examples/simple_form_composer/data/data.json rename to examples/simple_form_composer/data/simple/data.json diff --git a/examples/simple_form_composer/hydra_configs/conf/dynamic_example_local_mock.yaml b/examples/simple_form_composer/hydra_configs/conf/dynamic_example_local_mock.yaml new file mode 100644 index 000000000..dfff4e1c8 --- /dev/null +++ b/examples/simple_form_composer/hydra_configs/conf/dynamic_example_local_mock.yaml @@ -0,0 +1,25 @@ +#@package _global_ + +# 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. + +defaults: + - /mephisto/blueprint: static_react_task + - /mephisto/architect: local + - /mephisto/provider: mock + +mephisto: + blueprint: + data_json: ${task_dir}/data/dynamic/data.json + task_source: ${task_dir}/webapp/build/bundle.js + link_task_source: false + extra_source_dir: ${task_dir}/webapp/src/static + units_per_assignment: 1 + task: + task_name: simple_form_composer + task_title: "Example how to easily create simple form-based Tasks" + task_description: "In this Task, we use FormComposer feature." + task_reward: 0 + task_tags: "test,simple,form" + force_rebuild: true diff --git a/examples/simple_form_composer/hydra_configs/conf/example_local_mock.yaml b/examples/simple_form_composer/hydra_configs/conf/example_local_mock.yaml index cf4c93355..0f9d596e9 100644 --- a/examples/simple_form_composer/hydra_configs/conf/example_local_mock.yaml +++ b/examples/simple_form_composer/hydra_configs/conf/example_local_mock.yaml @@ -11,7 +11,7 @@ defaults: mephisto: blueprint: - data_json: ${task_dir}/data/data.json + data_json: ${task_dir}/data/simple/data.json task_source: ${task_dir}/webapp/build/bundle.js link_task_source: false extra_source_dir: ${task_dir}/webapp/src/static diff --git a/examples/simple_form_composer/run_task_dynamic.py b/examples/simple_form_composer/run_task_dynamic.py new file mode 100644 index 000000000..069185929 --- /dev/null +++ b/examples/simple_form_composer/run_task_dynamic.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +# 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 os + +from omegaconf import DictConfig + +from mephisto.generators.form_composer.configs_validation.extrapolated_config import ( + create_extrapolated_config +) +from mephisto.operations.operator import Operator +from mephisto.tools.scripts import build_custom_bundle +from mephisto.tools.scripts import task_script + + +@task_script(default_config_file="dynamic_example_local_mock") +def main(operator: Operator, cfg: DictConfig) -> None: + task_dir = cfg.task_dir + + build_custom_bundle( + task_dir, + force_rebuild=cfg.mephisto.task.force_rebuild, + post_install_script=cfg.mephisto.task.post_install_script, + ) + + operator.launch_task_run(cfg.mephisto) + operator.wait_for_runs_then_shutdown(skip_input=True, log_rate=30) + + +def create_data_config(): + app_path = os.path.dirname(os.path.abspath(__file__)) + data_path = os.path.join(app_path, "data") + + extrapolated_form_config_path = os.path.join(data_path, "dynamic", "data.json") + form_config_path = os.path.join(data_path, "dynamic", "form_config.json") + tokens_values_config_path = os.path.join(data_path, "dynamic", "tokens_values_config.json") + + create_extrapolated_config( + form_config_path=form_config_path, + tokens_values_config_path=tokens_values_config_path, + combined_config_path=extrapolated_form_config_path, + ) + + +if __name__ == "__main__": + create_data_config() + main() diff --git a/mephisto/README.md b/mephisto/README.md index 0c50b1d59..6806c1bab 100644 --- a/mephisto/README.md +++ b/mephisto/README.md @@ -260,18 +260,3 @@ or simply embed that command into your docker-compose entrypoint script. ## Process results Final steps of reviewing worker submissions and exporting the results will be same as described under sample Mephisto project runs. - -## Launch Auto-composed forms - -1. Create `data.json` config in `mephisto/generators/form_composer/data`. An example is found in `examples/simple_form_composer/data/data.json` file -2. Run a task with command: - - Locally `mephisto form_composer` - - Using Docker Compose - ```shell - docker-compose -f docker/docker-compose.dev.yml run \ - --build \ - --publish 8081:8000 \ - --rm mephisto_dc \ - mephisto form_composer - ``` -3. Open in Browser page: http://localhost:3001/ diff --git a/mephisto/client/README.md b/mephisto/client/README.md index 7ca947e54..bf2134598 100644 --- a/mephisto/client/README.md +++ b/mephisto/client/README.md @@ -6,14 +6,69 @@ TODO: [form-builder-app] Complete this reamde with details about each command -### Commands - -1. `mephisto config` -2. `mephisto check` -3. `mephisto requesters` -4. `mephisto register` -5. `mephisto wut` -6. `mephisto scripts` -7. `mephisto metrics` -8. `mephisto review_app` -9. `mephisto form_composer` +## Commands + +- [mephisto config](#mephisto-config) +- [mephisto check](#mephisto-check) +- [mephisto requesters](#mephisto-requesters) +- [mephisto register](#mephisto-register) +- [mephisto wut](#mephisto-wut) +- [mephisto scripts](#mephisto-scripts) +- [mephisto metrics](#mephisto-metrics) +- [mephisto review_app](#mephisto-reviewapp) +- [mephisto form_composer](#mephisto-formcomposer) + + +### mephisto config + +Set up a data directory where the results of your crowdsourcing tasks will be stored. +For more details you can consult [Setup section in quickstart.md](docs/web/docs/guides/quickstart.md#setup). + + +### mephisto check + +Check that everything is set up correctly. +For more details you can consult [Setup section in quickstart.md](docs/web/docs/guides/quickstart.md#setup). + + +### mephisto requesters + +Use the command to see registered requesters. +For more details you can consult [Docs for Requesters](docs/web/docs/reference/requesters.md#requesters). + + +### mephisto register + +Use the command to register requesters a new requester. +For more details you can consult [Docs for Requesters](docs/web/docs/reference/requesters.md#requesters) and +[Setup section in quickstart.md](docs/web/docs/guides/quickstart.md#setup). + + +### mephisto wut + +Dig for more information about specific configuration arguments. +For more details you can consult [First Task Tutorial](docs/web/docs/guides/tutorials/first_task.md#21-discovering-options-with-mephisto-wut). + + +### mephisto scripts + +Run scripts from `mephisto/scripts` directory. + + +### mephisto metrics + +Extension to view task health metrics via dashboard using [Prometheus](https://prometheus.io/) and [Grafana](https://grafana.com/oss/grafana/). +For more details you can consult [Docs for Metrics](docs/web/docs/guides/how_to_use/efficiency_organization/metrics_dashboarding.md). + + +### mephisto review_app + +Run the Web-application to review completed Mephisto tasks. +The UI is fairly intuitive, and for more details you can consult [README.md for TaskReview app](mephisto/review_app/README.md). + + +### mephisto form_composer + +Application to run simple form-based tasks. +One thing that you need is to provide configs with fields that you need for your task. +For more details you can consult [Docs for Metrics](docs/web/docs/guides/how_to_use/efficiency_organization/metrics_dashboarding.md). diff --git a/mephisto/generators/form_composer/README.md b/mephisto/generators/form_composer/README.md index 742d73fa9..6e24c1e4f 100644 --- a/mephisto/generators/form_composer/README.md +++ b/mephisto/generators/form_composer/README.md @@ -19,23 +19,126 @@ and then the below commands. #### Installation -```bash +```shell cd /mephisto/packages/react-form-composer && npm install --save react-form-composer ``` #### Building -```bash +```shell cd /mephisto/packages/react-form-composer && npm run build ``` +#### Launching +```shell +mephisto form_composer +``` + +Using Docker Compose +```shell +docker-compose -f docker/docker-compose.dev.yml run \ + --build \ + --publish 8081:8000 \ + --rm mephisto_dc \ + mephisto form_composer +``` + +Open in Browser page: http://localhost:3001/ + --- ## How to configure -You will need to provide Form Builder with a JSON configuration of your form fields. -The config file name should be `data.json`, and it should contain a single-item JSON array with one key `form`. +You will need to provide Form Composer with a JSON configuration of your form fields. +The form config file name should be `form_config.json`, and it should contain a JSON object with one key `form`. +If you want to use more than one unit configuration and pass different values in form, you need to add `tokens_values_config.json` as well. +This config is a JSON array object with JSON object items with one key `tokens_values` that is object where is a key is a token and its value is value that will be the substitution for this token in form. -An example is found in `examples/simple_form_composer/data/data.json` file. +Config examples: + - form config is found in `examples/simple_form_composer/data/dynamic/form_config.json` file + - tokens values config is found in `examples/simple_form_composer/data/dynamic/tokens_values_config.json` file + - resulting config is found in `examples/simple_form_composer/data/dynamic/data.json` file + +Shortened example to ha more clear view before we dig deeper: +```json +[ + { + "form": { + "title": "Form example", + "instruction": "Please answer all questions to the best of your ability as part of our study.", + "sections": [ + // Two sections + { + "name": "section_about", + "title": "About you", + "instruction": "Please introduce yourself. We would like to know more about your background, personal information, etc.", + "fieldsets": [ + // Two fieldsets + { + "title": "Personal information", + "instruction": "", + "rows": [ + // Two 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": "Please use your legal name" + }, + { ... } + ] + }, + { + "title": "Cultural background", + "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", + "rows": [ + // One row + { + "fields": [ + { + ... + "multiple": false, + "options": [ + ... + { + "label": "United States of America", + "value": "USA" + }, + ... + ], + "type": "select", + ... + } + ] + } + ], + "help": "This information will help us compile study statistics" + } + ] + }, + { ... } + ], + "submit_button": { + "text": "Submit", + "tooltip": "Submit form" + } + } + } +] +``` --- @@ -158,10 +261,150 @@ The most important attributes are: `label`, `name`, `type`, `validators` ## Dynamic form config -TBD +If you wish to slightly vary form instructions within the same Task (e.g. show different images or different text), you should use a dynamic form config. + +--- + +#### Dynamic form config files + +Dynamic form config consists of two parts: + +- `form_config.json`: tokenized form config - same as non-dynamic form config, except it may contain tokens within certain fields +- `tokens_values_config.json`: file containing sets of token values, where each set is plugged into a dynamic form config to generate its form version (each form version will be completed by `units_per_assignment` different workers). + + +###### Extrapolated config + +During bulding a Task with dynamic form config, the resulting config containing all form vesions will be placed in `data.json` file (next to `form_config.json` file). + +- In your YAML Task config, always refer to the extrapolated config file `data.json` (not the foorm config file) +- Every time you re-run Form Composer, `data.json` file will be overwritten +- If you're writing your own application that includes Form Composer, use `mephisto.generators.form_composer.configs_validation.extrapolated_config.create_extrapolated_config` function to generate the extrapolated `data.json` config + + +###### Custom form versions + +Suppose your form variations go beyond slight text changes (e.g. you wish to add a fieldset in one version of form config). In that case: + +- Create your own `data.json` file manually (it will be a JSON list of copy-pasted individual form config versions) +- You don't need to create `form_config.json` and `tokens_values_config.json` files +- Do not call `mephisto.generators.form_composer.configs_validation.extrapolated_config.create_extrapolated_config` function in your code (you already have a `data.json` file ready) + + +--- + +#### Tokens extrapolation + +A token is a named text placeholder that gets replaced ("extrapolated") by values specified in `tokens_values_config.json` (each set of `tokens_values` specifies a form version, and contains one such value). + +Token placeholders within a field looks like so: `{{TOKEN_NAME}}` + +Tokens can be placed within the following fields of a dynamic form config: + +- `help` +- `instruction` +- `label` +- `title` +- `tooltip` + + +When reusing a token with same name in different form fields (at any level of form config), you should specify it in each `tokens_values` just once, for convenience. (This also means that token names must be unique across the entire form config.) + +--- + +#### Config files example + + +###### Form config + +Here's how fields with tokens look like in `form_config.json` file: + +```json +{ + ... + "instruction": "Rate {{actor}}'s performance in movie '{{movie_name}}'", + ... + "help": "Please only consider the movie '{{movie_name}}'", + ... +} +... +{ + ... + "instruction": "Rate the plot in movie '{{movie_name}}'?", + ... +} +``` + + +###### Token values config + +Here's how token values are specified `tokens_values_config.json` file: + +```json +[ + { + "tokens_values": { + "actor": "Carrie Fisher", + "movie_name": "Star Wars" + } + }, + { + "tokens_values": { + "actor": "Keanu Reeves", + "movie_name": "The Matrix" + } + } +] +``` + + +###### Extrapolated config + +This is how resulting `data.json` file will look like, after form fields from `form_config.json` get extrapolated with values from `tokens_values_config.json`: + +```json +// First extrapolated form version +{ + ... + "instruction": "Rate Carrie Fisher's performance in movie 'Star Wars'", + ... + "help": "Please only consider the movie 'Star Wars'", + ... +} +... +{ + ... + "instruction": "Rate the plot in movie 'Star Wars'?", + ... +}, +// Second extrapolated form version +{ + ... + "instruction": "Rate Keanu Reeves's performance in movie 'The Matrix'", + ... + "help": "Please only consider the movie 'The Matrix'", + ... +} +... +{ + ... + "instruction": "Rate the plot in movie 'The Matrix'?", + ... +} +``` + +Once a Task is launched, each of these two form versions will be completed `units_per_assignment` times (by different workers) --- ## Custom field handlers TBD + +--- + +## Live Examples + +You can investigate live examples of Form Composer in `examples/simple_form_composer` directory, + +For more details on how to run these examples, refer to this [README.md](examples/simple_form_composer/README.md). diff --git a/mephisto/generators/form_composer/configs_validation/config_validation_constants.py b/mephisto/generators/form_composer/configs_validation/config_validation_constants.py index 12744180f..52d8fcb3c 100644 --- a/mephisto/generators/form_composer/configs_validation/config_validation_constants.py +++ b/mephisto/generators/form_composer/configs_validation/config_validation_constants.py @@ -174,4 +174,10 @@ str: "String", } -ATTRS_SUPPORTING_TOKENS = ["help", "instruction", "title"] +ATTRS_SUPPORTING_TOKENS = [ + "help", + "instruction", + "label", + "title", + "tooltip", +] diff --git a/packages/react-form-composer/src/FormComposer/validation/errorMessages.js b/packages/react-form-composer/src/FormComposer/validation/errorMessages.js index 428a40c82..ebdf6edd6 100644 --- a/packages/react-form-composer/src/FormComposer/validation/errorMessages.js +++ b/packages/react-form-composer/src/FormComposer/validation/errorMessages.js @@ -1,3 +1,9 @@ +/* + * 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. + */ + export const MAX_LENGTH_ERROR_MESSAGE_KEY = "max-length"; export const MAX_LENGTH_ITEMS_ERROR_MESSAGE_KEY = "max-length-items"; export const MIN_LENGTH_ERROR_MESSAGE_KEY = "min-length"; diff --git a/packages/react-form-composer/src/FormComposer/validation/helpers.js b/packages/react-form-composer/src/FormComposer/validation/helpers.js index 0c2432845..22cf4cefd 100644 --- a/packages/react-form-composer/src/FormComposer/validation/helpers.js +++ b/packages/react-form-composer/src/FormComposer/validation/helpers.js @@ -1,3 +1,9 @@ +/* + * 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 { REQUIRED_ERROR_MESSAGE_KEY } from "./errorMessages"; import { validatorFunctionsByConfigName } from "./validatorLookup"; diff --git a/packages/react-form-composer/src/FormComposer/validation/validatorLookup.js b/packages/react-form-composer/src/FormComposer/validation/validatorLookup.js index 54a2f1a9a..86fd39f07 100644 --- a/packages/react-form-composer/src/FormComposer/validation/validatorLookup.js +++ b/packages/react-form-composer/src/FormComposer/validation/validatorLookup.js @@ -1,3 +1,9 @@ +/* + * 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 fieldIsRequired from "./validators/fieldIsRequired"; import maxLengthSatisfied from "./validators/maxLengthSatisfied"; import minLengthSatisfied from "./validators/minLengthSatisfied"; diff --git a/packages/react-form-composer/src/FormComposer/validation/validators/fieldIsRequired.js b/packages/react-form-composer/src/FormComposer/validation/validators/fieldIsRequired.js index 579ac541c..392262376 100644 --- a/packages/react-form-composer/src/FormComposer/validation/validators/fieldIsRequired.js +++ b/packages/react-form-composer/src/FormComposer/validation/validators/fieldIsRequired.js @@ -1,3 +1,9 @@ +/* + * 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 { REQUIRED_ERROR_MESSAGE_KEY, validationErrorMessagesByName } from "../errorMessages"; /** diff --git a/packages/react-form-composer/src/FormComposer/validation/validators/maxLengthSatisfied.js b/packages/react-form-composer/src/FormComposer/validation/validators/maxLengthSatisfied.js index 71e5778bd..59914df6b 100644 --- a/packages/react-form-composer/src/FormComposer/validation/validators/maxLengthSatisfied.js +++ b/packages/react-form-composer/src/FormComposer/validation/validators/maxLengthSatisfied.js @@ -1,3 +1,9 @@ +/* + * 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 { MAX_LENGTH_ERROR_MESSAGE_KEY, MAX_LENGTH_ITEMS_ERROR_MESSAGE_KEY, diff --git a/packages/react-form-composer/src/FormComposer/validation/validators/minLengthSatisfied.js b/packages/react-form-composer/src/FormComposer/validation/validators/minLengthSatisfied.js index a6bca53f1..f4406ef6c 100644 --- a/packages/react-form-composer/src/FormComposer/validation/validators/minLengthSatisfied.js +++ b/packages/react-form-composer/src/FormComposer/validation/validators/minLengthSatisfied.js @@ -1,3 +1,9 @@ +/* + * 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 { MIN_LENGTH_ERROR_MESSAGE_KEY, MIN_LENGTH_ITEMS_ERROR_MESSAGE_KEY, diff --git a/packages/react-form-composer/src/FormComposer/validation/validators/regexpSatisfied.js b/packages/react-form-composer/src/FormComposer/validation/validators/regexpSatisfied.js index b55815739..da1bdc75b 100644 --- a/packages/react-form-composer/src/FormComposer/validation/validators/regexpSatisfied.js +++ b/packages/react-form-composer/src/FormComposer/validation/validators/regexpSatisfied.js @@ -1,3 +1,9 @@ +/* + * 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 { REGEXP_ERROR_MESSAGE_KEY, validationErrorMessagesByName } from "../errorMessages"; /**