| """ |
| Interactive Task Creation CLI Module |
| |
| Provides an interactive command-line interface for creating new annotation tasks. |
| Guides users through configuration and generates a YAML config file. |
| """ |
|
|
| import os |
|
|
| import click |
| import yaml |
|
|
|
|
| |
| |
| COMMON_ANNOTATION_TYPES = [ |
| "radio", |
| "multiselect", |
| "text", |
| "likert", |
| "slider", |
| "span", |
| "bws", |
| "pairwise", |
| "ranking", |
| ] |
|
|
|
|
| def create_task_cli(): |
| """ |
| Interactive task creation wizard. |
| |
| Walks the user through server setup, data files, annotation schemes, |
| and output settings, then writes a YAML config file. |
| """ |
| click.echo("Welcome to the Potato annotation task creation wizard!") |
| click.echo("This will generate a .yaml config file for your annotation task.") |
| click.echo("You can edit the file afterwards to add advanced features.\n") |
|
|
| click.echo("— Server setup —\n") |
|
|
| task_name = click.prompt("Task name (shown to annotators)", default="My Annotation Task") |
| port = click.prompt("Server port", default=8000, type=click.IntRange(1, 65535)) |
|
|
| click.echo("\n— Data files —\n") |
|
|
| data_files = [] |
| fname = click.prompt("Path to your data file (JSONL)") |
| data_files.append(fname) |
| while click.confirm("Add another data file?", default=False): |
| fname = click.prompt("Path to data file") |
| data_files.append(fname) |
|
|
| id_key = click.prompt("Which field in the data is the item ID?", default="id") |
| text_key = click.prompt("Which field is the item text?", default="text") |
|
|
| click.echo("\n— Annotation schemes —\n") |
|
|
| annotation_schemes = [] |
| while True: |
| atype = click.prompt( |
| "Annotation type", |
| type=click.Choice(COMMON_ANNOTATION_TYPES, case_sensitive=False), |
| ) |
| name = click.prompt("Internal name for this scheme (used in output)") |
| desc = click.prompt("Description/question shown to annotators") |
|
|
| scheme = { |
| "annotation_type": atype, |
| "name": name, |
| "description": desc, |
| } |
|
|
| if atype in ("radio", "multiselect"): |
| labels = [] |
| click.echo("Enter labels one at a time (empty line to finish):") |
| while True: |
| label = click.prompt(" Label", default="", show_default=False) |
| if not label: |
| break |
| labels.append(label) |
| scheme["labels"] = labels |
|
|
| elif atype == "likert": |
| size = click.prompt("Scale size", default=5, type=int) |
| min_label = click.prompt("Label for minimum", default="Strongly Disagree") |
| max_label = click.prompt("Label for maximum", default="Strongly Agree") |
| scheme["size"] = size |
| scheme["min_label"] = min_label |
| scheme["max_label"] = max_label |
|
|
| elif atype == "slider": |
| scheme["min_value"] = click.prompt("Minimum value", default=0, type=int) |
| scheme["max_value"] = click.prompt("Maximum value", default=100, type=int) |
|
|
| elif atype == "bws": |
| size = click.prompt("Tuple size (items shown at once)", default=4, type=int) |
| scheme["size"] = size |
|
|
| elif atype == "ranking": |
| labels = [] |
| click.echo("Enter options to rank (empty line to finish):") |
| while True: |
| label = click.prompt(" Option", default="", show_default=False) |
| if not label: |
| break |
| labels.append(label) |
| scheme["labels"] = labels |
|
|
| annotation_schemes.append(scheme) |
|
|
| if not click.confirm("\nAdd another annotation scheme?", default=False): |
| break |
|
|
| click.echo("\n— Output settings —\n") |
|
|
| output_dir = click.prompt("Output directory for annotations", default="annotation_output") |
| codebook_url = click.prompt("Annotation codebook URL (optional)", default="") |
|
|
| auto_export = click.confirm("Auto-export annotations in CSV/JSONL?", default=False) |
| export_format = None |
| if auto_export: |
| export_format = click.prompt( |
| "Export format", |
| type=click.Choice(["csv", "tsv", "jsonl"], case_sensitive=False), |
| default="csv", |
| ) |
|
|
| |
| config = { |
| "annotation_task_name": task_name, |
| "port": port, |
| "data_files": data_files, |
| "item_properties": { |
| "id_key": id_key, |
| "text_key": text_key, |
| }, |
| "annotation_schemes": annotation_schemes, |
| "output_annotation_dir": output_dir, |
| "annotation_codebook_url": codebook_url, |
| "user_config": { |
| "allow_all_users": True, |
| "users": [], |
| }, |
| } |
|
|
| if export_format: |
| config["export_annotation_format"] = export_format |
|
|
| click.echo("\n— Save config —\n") |
|
|
| config_file = click.prompt("Path for the config file", default="config.yaml") |
|
|
| if os.path.exists(config_file): |
| if not click.confirm(f"{config_file} already exists. Overwrite?", default=False): |
| click.echo("Aborted.") |
| return |
|
|
| with open(config_file, "w", encoding="utf-8") as f: |
| yaml.dump(config, f, default_flow_style=False, sort_keys=False, allow_unicode=True) |
|
|
| click.echo(f"\nConfig written to {config_file}") |
| click.echo(f"Start annotating with: potato start {config_file}") |
|
|