Skip to content
This repository was archived by the owner on Apr 3, 2025. It is now read-only.

Commit fc60794

Browse files
committed
[Form Builder] Added unittests for main components
1 parent af2d366 commit fc60794

25 files changed

+2787
-52
lines changed

examples/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ Putting it altogether, let's prepare and launch a task featuring a form containi
184184
mephisto review_app -h 0.0.0.0 -p 8000 -d True -f True
185185
```
186186

187+
_Note: if a package build was terminated/failed, or related source code was changed, FormComposer needs to be rebuilt with this command: `mephisto scripts form_composer rebuild_all_apps`._
188+
187189
---
188190

189191
# Your Mephisto project

mephisto/client/cli.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import mephisto.scripts.mturk.launch_makeup_hits as launch_makeup_hits_mturk
2929
import mephisto.scripts.mturk.print_outstanding_hit_status as print_outstanding_hit_status_mturk
3030
import mephisto.scripts.mturk.print_outstanding_hit_status as soft_block_workers_by_mturk_id_mturk
31+
import mephisto.scripts.form_composer.rebuild_all_apps as rebuild_all_apps_form_composer
3132
from mephisto.client.cli_commands import get_wut_arguments
3233
from mephisto.generators.form_composer.config_validation.task_data_config import (
3334
create_extrapolated_config
@@ -223,7 +224,7 @@ def print_non_markdown_list(items: List[str]):
223224
res += "\n * " + item
224225
return res
225226

226-
VALID_SCRIPT_TYPES = ["local_db", "heroku", "metrics", "mturk"]
227+
VALID_SCRIPT_TYPES = ["local_db", "heroku", "metrics", "mturk", "form_composer"]
227228
if script_type is None or script_type.strip() not in VALID_SCRIPT_TYPES:
228229
print("")
229230
raise click.UsageError(
@@ -247,6 +248,9 @@ def print_non_markdown_list(items: List[str]):
247248
"print_outstanding_hit_status",
248249
"soft_block_workers_by_mturk_id",
249250
]
251+
FORM_COMPOSER_VALID_SCRIPTS_NAMES = [
252+
"rebuild_all_apps",
253+
]
250254
script_type_to_scripts_data = {
251255
"local_db": {
252256
"valid_script_names": LOCAL_DB_VALID_SCRIPTS_NAMES,
@@ -275,10 +279,16 @@ def print_non_markdown_list(items: List[str]):
275279
MTURK_VALID_SCRIPTS_NAMES[0]: cleanup_mturk.main,
276280
MTURK_VALID_SCRIPTS_NAMES[1]: identify_broken_units_mturk.main,
277281
MTURK_VALID_SCRIPTS_NAMES[2]: launch_makeup_hits_mturk.main,
278-
MTURK_VALID_SCRIPTS_NAMES[3]: print_outstanding_hit_status_mturk.main,
282+
MTURK_VALID_SCRIPTS_NAMES[3]: rebuild_all_apps_form_composer.main,
279283
MTURK_VALID_SCRIPTS_NAMES[4]: soft_block_workers_by_mturk_id_mturk.main,
280284
},
281285
},
286+
"form_composer": {
287+
"valid_script_names": FORM_COMPOSER_VALID_SCRIPTS_NAMES,
288+
"scripts": {
289+
FORM_COMPOSER_VALID_SCRIPTS_NAMES[0]: rebuild_all_apps_form_composer.main,
290+
},
291+
},
282292
}
283293

284294
if script_name is None or (

mephisto/generators/form_composer/config_validation/form_config.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ def _duplicate_values_exist(unique_names: UniqueAttrsType, errors: List[str]) ->
5454
return is_valid
5555

5656

57-
def validate_form_config(config_json: dict) -> Tuple[bool, List[str]]:
57+
def validate_form_config(config_data: dict) -> Tuple[bool, List[str]]:
5858
is_valid = True
5959
errors = []
6060

61-
if not isinstance(config_json, dict):
61+
if not isinstance(config_data, dict):
6262
is_valid = False
6363
errors.append("Form config must be a key/value JSON Object.")
6464

65-
elif config_json.keys() != AVAILABLE_CONFIG_ATTRS.keys():
65+
elif config_data.keys() != AVAILABLE_CONFIG_ATTRS.keys():
6666
is_valid = False
6767
errors.append(
6868
f"Form config must contain only these attributes: "
@@ -77,10 +77,10 @@ def validate_form_config(config_json: dict) -> Tuple[bool, List[str]]:
7777
unique_names: UniqueAttrsType = {}
7878

7979
# Add main config level
80-
items_to_validate.append((config_json, "Config", AVAILABLE_CONFIG_ATTRS))
80+
items_to_validate.append((config_data, "Config", AVAILABLE_CONFIG_ATTRS))
8181

8282
# Add form
83-
form = config_json["form"]
83+
form = config_data["form"]
8484
items_to_validate.append((form, "form", AVAILABLE_FORM_ATTRS))
8585
_collect_values_for_unique_attrs_from_item(form, unique_names)
8686

mephisto/generators/form_composer/config_validation/separate_token_values_config.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from botocore.exceptions import BotoCoreError
1212
from botocore.exceptions import ClientError
1313
from botocore.exceptions import NoCredentialsError
14+
from rich import print
1415

1516
from mephisto.generators.form_composer.constants import TOKEN_END_SYMBOLS
1617
from mephisto.generators.form_composer.constants import TOKEN_START_SYMBOLS
@@ -22,17 +23,17 @@
2223

2324

2425
def validate_separate_token_values_config(
25-
config_json: Dict[str, List[str]],
26+
config_data: Dict[str, List[str]],
2627
) -> Tuple[bool, List[str]]:
2728
is_valid = True
2829
errors = []
2930

30-
if not isinstance(config_json, dict):
31+
if not isinstance(config_data, dict):
3132
is_valid = False
3233
errors.append("Config must be a key/value JSON Object.")
3334
return is_valid, errors
3435

35-
for i, token_values in enumerate(config_json.items()):
36+
for i, token_values in enumerate(config_data.items()):
3637
token, values = token_values
3738

3839
if not values:
@@ -48,7 +49,7 @@ def validate_separate_token_values_config(
4849
def update_separate_token_values_config_with_file_urls(
4950
url: str,
5051
separate_token_values_config_path: str,
51-
use_presigned_urls: bool,
52+
use_presigned_urls: bool = False,
5253
):
5354
try:
5455
files_locations = get_file_urls_from_s3_storage(url)

mephisto/generators/form_composer/config_validation/task_data_config.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,15 @@ def _set_tokens_in_form_config_item(item: dict, tokens_values: dict):
5656
def _collect_form_config_items_to_extrapolate(config_data: dict) -> List[dict]:
5757
items_to_extrapolate = []
5858

59+
if not isinstance(config_data, dict):
60+
return items_to_extrapolate
61+
5962
form = config_data["form"]
6063
items_to_extrapolate.append(form)
6164

65+
submit_button = form["submit_button"]
66+
items_to_extrapolate.append(submit_button)
67+
6268
sections = form["sections"]
6369
for section in sections:
6470
items_to_extrapolate.append(section)
@@ -125,7 +131,7 @@ def _extrapolate_tokens_in_form_config(config_data: dict, tokens_values: dict) -
125131

126132

127133
def _validate_tokens_in_both_configs(
128-
form_config_data, token_sets_values_config_data,
134+
form_config_data: dict, token_sets_values_config_data: List[dict],
129135
) -> Tuple[set, set, list]:
130136
tokens_from_form_config, tokens_in_unexpected_attrs_errors = (
131137
_collect_tokens_from_form_config(form_config_data)
@@ -245,21 +251,21 @@ def create_extrapolated_config(
245251
exit()
246252

247253

248-
def validate_task_data_config(config_json: List[dict]) -> Tuple[bool, List[str]]:
254+
def validate_task_data_config(config_data: List[dict]) -> Tuple[bool, List[str]]:
249255
is_valid = True
250256
errors = []
251257

252-
if not isinstance(config_json, list):
258+
if not isinstance(config_data, list):
253259
is_valid = False
254260
errors.append("Config must be a JSON Array.")
255261

256-
if config_json:
257-
if not all(config_json):
262+
if config_data:
263+
if not all(config_data):
258264
is_valid = False
259265
errors.append("Task data config must contain at least one non-empty item.")
260266

261267
# Validate each form version contained in task data config
262-
for item in config_json:
268+
for item in config_data:
263269
form_config_is_valid, form_config_errors = validate_form_config(item)
264270
if not form_config_is_valid:
265271
is_valid = False
@@ -375,11 +381,13 @@ def verify_form_composer_configs(
375381
print(f"\n[red]Provided Form Composer config files are invalid:[/red] {e}\n")
376382

377383

378-
def prepare_task_config_for_review_app(config: dict) -> dict:
379-
config = deepcopy(config)
384+
def prepare_task_config_for_review_app(config_data: dict) -> dict:
385+
config_data = deepcopy(config_data)
380386

381387
procedure_code_regex = r"\s*(.+?)\s*"
382-
tokens_from_inputs, _ = _collect_tokens_from_form_config(config, regex=procedure_code_regex)
388+
tokens_from_inputs, _ = _collect_tokens_from_form_config(
389+
config_data, regex=procedure_code_regex,
390+
)
383391

384392
url_from_rpocedure_code_regex = r"\(\"(.+?)\"\)"
385393
token_values = {}
@@ -396,5 +404,5 @@ def prepare_task_config_for_review_app(config: dict) -> dict:
396404
presigned_url = get_s3_presigned_url(url, S3_URL_EXPIRATION_MINUTES_MAX)
397405
token_values[token] = presigned_url
398406

399-
prepared_config = _extrapolate_tokens_in_form_config(config, token_values)
407+
prepared_config = _extrapolate_tokens_in_form_config(config_data, token_values)
400408
return prepared_config

mephisto/generators/form_composer/constants.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@
77

88
CONTENTTYPE_BY_EXTENSION = {
99
# Docs
10-
'csv': 'text/csv',
11-
'doc': 'application/msword',
12-
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
13-
'pdf': 'application/pdf',
10+
"csv": "text/csv",
11+
"doc": "application/msword",
12+
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
13+
"pdf": "application/pdf",
1414
# Images
15-
'bmp': 'image/bmp',
16-
'gif': 'image/gif',
17-
'heic': 'image/heic',
18-
'heif': 'image/heif',
19-
'jpeg': 'image/jpeg',
20-
'jpg': 'image/jpeg',
21-
'png': 'image/png',
15+
"bmp": "image/bmp",
16+
"gif": "image/gif",
17+
"heic": "image/heic",
18+
"heif": "image/heif",
19+
"jpeg": "image/jpeg",
20+
"jpg": "image/jpeg",
21+
"png": "image/png",
2222
# Videos
23-
'mkv': 'video/x-matroska',
24-
'mp4': 'video/mp4',
25-
'webm': 'video/webm',
23+
"mkv": "video/x-matroska",
24+
"mp4": "video/mp4",
25+
"webm": "video/webm",
2626
}
2727

2828
JSON_IDENTATION = 2

mephisto/review_app/server/api/views/unit_data_static_view.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from flask import Response
1111
from flask import send_from_directory
1212
from flask.views import MethodView
13+
from werkzeug.exceptions import NotFound
1314

1415
from mephisto.data_model.agent import Agent
1516
from mephisto.data_model.unit import Unit
@@ -55,5 +56,10 @@ def get(
5556
if filename_by_original_name:
5657
filename = filename_by_original_name
5758

58-
unit_data_folder = unit.get_assigned_agent().get_data_dir()
59+
agent = unit.get_assigned_agent()
60+
if not agent:
61+
app.logger.debug(f"No agent found for {unit}")
62+
raise NotFound("File not found")
63+
64+
unit_data_folder = agent.get_data_dir()
5965
return send_from_directory(unit_data_folder, filename)

mephisto/review_app/server/api/views/units_details_view.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ def get(self) -> dict:
5555
task_run: TaskRun = unit.get_task_run()
5656
has_task_source_review = bool(task_run.args.get("blueprint").get("task_source_review"))
5757

58-
inputs = unit_data.get("data", {}).get("inputs")
59-
outputs = unit_data.get("data", {}).get("outputs")
58+
inputs = unit_data.get("data", {}).get("inputs", {})
59+
outputs = unit_data.get("data", {}).get("outputs", {})
6060

6161
# In case if there is outdated code that returns `final_submission`
6262
# under `inputs` and `outputs` keys, we should use the value in side `final_submission`
@@ -67,9 +67,12 @@ def get(self) -> dict:
6767

6868
# Perform any dynamic action on task config for current unit
6969
# to make it the same as it looked like for a worker
70-
prepared_inputs = prepare_task_config_for_review_app(inputs)
70+
prepared_inputs = inputs
71+
if "form" in inputs:
72+
prepared_inputs = prepare_task_config_for_review_app(inputs)
7173

72-
unit_data_folder = unit.get_assigned_agent().get_data_dir()
74+
agent = unit.get_assigned_agent()
75+
unit_data_folder = agent.get_data_dir() if agent else None
7376

7477
units.append(
7578
{
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Meta Platforms and its affiliates.
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.

0 commit comments

Comments
 (0)