From 8717822de378d7464d6777e24bf3761d25e070d8 Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 4 Oct 2024 15:49:33 -0400 Subject: [PATCH] reformat --- src/social_norms_trees/behavior_library.py | 8 +- src/social_norms_trees/custom_node_library.py | 3 +- src/social_norms_trees/mutate_tree.py | 116 ++++++++---------- src/social_norms_trees/serialize_tree.py | 57 +++++---- src/social_norms_trees/ui_wrapper.py | 79 ++++++------ 5 files changed, 128 insertions(+), 135 deletions(-) diff --git a/src/social_norms_trees/behavior_library.py b/src/social_norms_trees/behavior_library.py index c479081c..58c784f4 100644 --- a/src/social_norms_trees/behavior_library.py +++ b/src/social_norms_trees/behavior_library.py @@ -1,5 +1,7 @@ class BehaviorLibrary: def __init__(self, behavior_list): - self.behaviors = behavior_list - self.behavior_from_display_name = {behavior["display_name"]: behavior for behavior in behavior_list} - self.behavior_from_id = {behavior["id"]: behavior for behavior in behavior_list} \ No newline at end of file + self.behaviors = behavior_list + self.behavior_from_display_name = { + behavior["display_name"]: behavior for behavior in behavior_list + } + self.behavior_from_id = {behavior["id"]: behavior for behavior in behavior_list} diff --git a/src/social_norms_trees/custom_node_library.py b/src/social_norms_trees/custom_node_library.py index 0a95b73c..4b58724e 100644 --- a/src/social_norms_trees/custom_node_library.py +++ b/src/social_norms_trees/custom_node_library.py @@ -1,5 +1,6 @@ import py_trees + class CustomBehavior(py_trees.behaviours.Dummy): def __init__(self, name, id_, display_name): super().__init__(name) @@ -16,5 +17,3 @@ def __init__(self, name, id_, display_name, children=None, memory=False): # id of the behavior within the behavior library (persists) # but also the unique id for the behavior within the tree (in case there are multiple instances of # the behavior in one tree) - - diff --git a/src/social_norms_trees/mutate_tree.py b/src/social_norms_trees/mutate_tree.py index 82e3eb2e..3146f154 100644 --- a/src/social_norms_trees/mutate_tree.py +++ b/src/social_norms_trees/mutate_tree.py @@ -149,11 +149,15 @@ def format_children_with_indices(composite: py_trees.composites.Composite) -> st output = label_tree_lines(composite, index_strings) return output + def format_parents_with_indices(composite: py_trees.composites.Composite) -> str: index_strings = [] i = 0 for b in iterate_nodes(composite): - if b.__class__.__name__ == "CustomSequence" or b.__class__.__name__ == "CustomSelector": + if ( + b.__class__.__name__ == "CustomSequence" + or b.__class__.__name__ == "CustomSelector" + ): index_strings.append(str(i)) else: index_strings.append("_") @@ -164,9 +168,9 @@ def format_parents_with_indices(composite: py_trees.composites.Composite) -> str def format_tree_with_indices( - tree: py_trees.behaviour.Behaviour, - show_root: bool = False, -) -> tuple[str, List[str]]: + tree: py_trees.behaviour.Behaviour, + show_root: bool = False, +) -> tuple[str, List[str]]: """ Examples: >>> print(format_tree_with_indices(py_trees.behaviours.Dummy())) @@ -199,12 +203,12 @@ def format_tree_with_indices( index = 0 for i, node in enumerate_nodes(tree): if i == 0 and not show_root: - index_strings.append('_') + index_strings.append("_") else: index_strings.append(str(index)) index += 1 output = label_tree_lines(tree, index_strings) - + return output, index_strings[1:] @@ -224,6 +228,7 @@ def prompt_identify_node( node = next(islice(iterate_nodes(tree), node_index, node_index + 1)) return node + def prompt_identify_parent_node( tree: py_trees.behaviour.Behaviour, message: str = "Which position?", @@ -237,7 +242,7 @@ def prompt_identify_parent_node( text=text, type=int, ) - + node = next(islice(iterate_nodes(tree), node_index, node_index + 1)) return node @@ -257,7 +262,7 @@ def prompt_identify_tree_iterator_index( node_index = click.prompt( text=text, type=click.Choice(index_options, case_sensitive=False), - show_choices=False + show_choices=False, ) return int(node_index) @@ -345,22 +350,19 @@ def remove_node(tree: T, node: Optional[py_trees.behaviour.Behaviour] = None) -> f"{node}'s parent is None, so we can't remove it. You can't remove the root node." ) action_log = {} - return tree, + return (tree,) elif isinstance(parent_node, py_trees.composites.Composite): parent_node.remove_child(node) action_log = { "type": "remove_node", "nodes": [ - { - "id_": node.id_, - "display_name": node.display_name - }, + {"id_": node.id_, "display_name": node.display_name}, ], - "timestamp": datetime.now().isoformat(), - } + "timestamp": datetime.now().isoformat(), + } else: raise NotImplementedError() - + return tree, action_log @@ -394,7 +396,6 @@ def move_node( node.parent.remove_child(node) new_parent.insert_child(node, index) - if not internal_call: action_log = { "type": "move_node", @@ -404,10 +405,10 @@ def move_node( "display_name": node.display_name, }, ], - "timestamp": datetime.now().isoformat(), + "timestamp": datetime.now().isoformat(), } return tree, action_log - + return tree @@ -481,13 +482,8 @@ def exchange_nodes( } ) else: - nodes.append( - { - "id": node0.id_, - "display_name": node0.display_name - } - ) - + nodes.append({"id": node0.id_, "display_name": node0.display_name}) + if node1.__class__.__name__ != "CustomBehavior": nodes.append( { @@ -495,35 +491,27 @@ def exchange_nodes( } ) else: - nodes.append( - { - "id": node1.id_, - "display_name": node1.display_name - } - ) + nodes.append({"id": node1.id_, "display_name": node1.display_name}) action_log = { "type": "exchange_nodes", "nodes": nodes, - "timestamp": datetime.now().isoformat(), + "timestamp": datetime.now().isoformat(), } return tree, action_log def prompt_select_node(behavior_library, text): - - for idx, tree_name in enumerate(behavior_library.behavior_from_display_name.keys(), 1): - print(f"{idx}. {tree_name}") + for idx, tree_name in enumerate( + behavior_library.behavior_from_display_name.keys(), 1 + ): + print(f"{idx}. {tree_name}") choices = [str(i + 1) for i in range(len(behavior_library.behaviors))] - node_index = click.prompt( - text=text, - type=click.Choice(choices), - show_choices=False - ) + node_index = click.prompt(text=text, type=click.Choice(choices), show_choices=False) + + node_key = list(behavior_library.behavior_from_display_name.keys())[node_index - 1] - node_key = list(behavior_library.behavior_from_display_name.keys())[node_index-1] - return behavior_library.behavior_from_display_name[node_key] @@ -538,40 +526,38 @@ def add_node( """ - - behavior = prompt_select_node(behavior_library, f"Which behavior do you want to add?") - - if behavior['type'] == "Behavior": + behavior = prompt_select_node( + behavior_library, f"Which behavior do you want to add?" + ) + + if behavior["type"] == "Behavior": new_node = CustomBehavior( - name=behavior['display_name'], - id_=behavior['id'], - display_name=behavior['display_name'] - ) - - elif behavior['type'] == "Sequence": + name=behavior["display_name"], + id_=behavior["id"], + display_name=behavior["display_name"], + ) + + elif behavior["type"] == "Sequence": new_node = CustomSequence( - name=behavior['display_name'], - id_=behavior['id'], - display_name=behavior['display_name'], - ) - + name=behavior["display_name"], + id_=behavior["id"], + display_name=behavior["display_name"], + ) + new_parent = prompt_identify_parent_node( tree, f"What should its parent be?", display_nodes=True ) index = prompt_identify_child_index(new_parent) - + assert isinstance(new_parent, py_trees.composites.Composite) new_parent.insert_child(new_node, index) - + action_log = { "type": "add_node", - "node": { - "id": new_node.id_, - "display_name": new_node.display_name - }, - "timestamp": datetime.now().isoformat(), + "node": {"id": new_node.id_, "display_name": new_node.display_name}, + "timestamp": datetime.now().isoformat(), } return tree, action_log diff --git a/src/social_norms_trees/serialize_tree.py b/src/social_norms_trees/serialize_tree.py index 1cf50fcb..a3424f90 100644 --- a/src/social_norms_trees/serialize_tree.py +++ b/src/social_norms_trees/serialize_tree.py @@ -13,59 +13,62 @@ def serialize_node(node): return serialize_node(tree) + def deserialize_tree(tree, behavior_library): def deserialize_node(node): - assert type(node['type'] == str), ( + assert type(node["type"] == str), ( f"\nThere was an invalid configuration detected in the inputted behavior tree: " f"Invalid type for node attribute 'type' found for node '{node['name']}'. " f"Please ensure that the 'name' attribute is a string." ) - assert type(node['name'] == str), ( + assert type(node["name"] == str), ( f"\nThere was an invalid configuration detected in the inputted behavior tree: " f"Invalid type for node attribute 'name' found for node '{node['name']}'. " f"Please ensure that the 'name' attribute is a string." ) - node_type = node['type'] + node_type = node["type"] assert node_type in ["Sequence", "Selector", "Behavior"], ( f"\nThere was an invalid configuration detected in the inputted behavior tree: " f"Invalid node type '{node_type}' found for node '{node['name']}'. " f"Please ensure that all node types are correct and supported." ) - behavior = behavior_library.behavior_from_display_name[node['name']] + behavior = behavior_library.behavior_from_display_name[node["name"]] - if node_type == 'Sequence': + if node_type == "Sequence": + children = [deserialize_node(child) for child in node["children"]] - children = [deserialize_node(child) for child in node['children']] - if behavior: return CustomSequence( - name=behavior['display_name'], - id_=behavior['id'], - display_name=behavior['display_name'], - children=children + name=behavior["display_name"], + id_=behavior["id"], + display_name=behavior["display_name"], + children=children, ) else: - raise ValueError(f"Behavior {node['name']} not found in behavior library") - - #TODO: node type Selector - - elif node_type == 'Behavior': - - assert ('children' not in node or len(node['children']) == 0), ( - f"\nThere was an invalid configuration detected in the inputted behavior tree: " - f"Children were detected for Behavior type node '{node['name']}': " - f"Behavior nodes should not have any children. Please check the structure of your behavior tree." - ) + raise ValueError( + f"Behavior {node['name']} not found in behavior library" + ) + + # TODO: node type Selector + + elif node_type == "Behavior": + assert "children" not in node or len(node["children"]) == 0, ( + f"\nThere was an invalid configuration detected in the inputted behavior tree: " + f"Children were detected for Behavior type node '{node['name']}': " + f"Behavior nodes should not have any children. Please check the structure of your behavior tree." + ) if behavior: return CustomBehavior( - name=behavior['display_name'], - id_=behavior['id'], - display_name=behavior['display_name'] + name=behavior["display_name"], + id_=behavior["id"], + display_name=behavior["display_name"], ) else: - raise ValueError(f"Behavior {node['name']} not found in behavior library") + raise ValueError( + f"Behavior {node['name']} not found in behavior library" + ) - return deserialize_node(tree) \ No newline at end of file + return deserialize_node(tree) diff --git a/src/social_norms_trees/ui_wrapper.py b/src/social_norms_trees/ui_wrapper.py index 53240683..0d048782 100644 --- a/src/social_norms_trees/ui_wrapper.py +++ b/src/social_norms_trees/ui_wrapper.py @@ -7,7 +7,12 @@ import py_trees import traceback -from social_norms_trees.mutate_tree import move_node, exchange_nodes, remove_node, add_node +from social_norms_trees.mutate_tree import ( + move_node, + exchange_nodes, + remove_node, + add_node, +) from social_norms_trees.serialize_tree import serialize_tree, deserialize_tree from social_norms_trees.behavior_library import BehaviorLibrary @@ -22,6 +27,7 @@ def load_db(db_file): else: return {} + def save_db(db, db_file): """Saves the Python dictionary back to db.json.""" @@ -30,8 +36,8 @@ def save_db(db, db_file): with open(db_file, "w") as f: json.dump(db, f, indent=4) -def experiment_setup(db, origin_tree): +def experiment_setup(db, origin_tree): print("\n") participant_id = participant_login() @@ -43,7 +49,6 @@ def experiment_setup(db, origin_tree): def participant_login(): - participant_id = click.prompt("Please enter your participant id", type=str) return participant_id @@ -52,18 +57,17 @@ def participant_login(): def load_resources(file_path): try: print(f"\nLoading behavior tree and behavior library from {file_path}...\n") - with open(file_path, 'r') as file: + with open(file_path, "r") as file: resources = json.load(file) - + except json.JSONDecodeError: raise ValueError("Error") except Exception: raise RuntimeError("Error") - - behavior_tree = resources.get('behavior_tree') - behavior_list = resources.get('behavior_library') - context_paragraph = resources.get('context') + behavior_tree = resources.get("behavior_tree") + behavior_list = resources.get("behavior_library") + context_paragraph = resources.get("context") behavior_library = BehaviorLibrary(behavior_list) @@ -72,11 +76,11 @@ def load_resources(file_path): print("Loading success.") return behavior_tree, behavior_library, context_paragraph -def initialize_experiment_record(db, participant_id, origin_tree): +def initialize_experiment_record(db, participant_id, origin_tree): experiment_id = str(uuid.uuid4()) - #TODO: look into python data class + # TODO: look into python data class experiment_record = { "experiment_id": experiment_id, @@ -92,29 +96,27 @@ def initialize_experiment_record(db, participant_id, origin_tree): def run_experiment(db, origin_tree, experiment_id, behavior_library): - # Loop for the actual experiment part, which takes user input to decide which action to take print("\nExperiment beginning...\n") try: - while(True): - + while True: print(py_trees.display.ascii_tree(origin_tree)) user_choice = click.prompt( "Would you like to perform an action on the behavior tree?", show_choices=True, - type=click.Choice(['y', 'n'], case_sensitive=False), + type=click.Choice(["y", "n"], case_sensitive=False), ) - - if user_choice == 'y': + + if user_choice == "y": action = click.prompt( - "1. move node\n" + - "2. exchange node\n" + - "3. remove node\n" + - "4. add node\n" + - "Please select an action to perform on the behavior tree", + "1. move node\n" + + "2. exchange node\n" + + "3. remove node\n" + + "4. add node\n" + + "Please select an action to perform on the behavior tree", type=click.IntRange(min=1, max=4), - show_choices=True + show_choices=True, ) if action == 1: @@ -133,51 +135,52 @@ def run_experiment(db, origin_tree, experiment_id, behavior_library): db[experiment_id]["action_history"].append(action_log) else: - print("Invalid choice, please select a valid number (1, 2, 3, or 4).\n") + print( + "Invalid choice, please select a valid number (1, 2, 3, or 4).\n" + ) - #user_choice == "n", end simulation run + # user_choice == "n", end simulation run else: break except Exception: - print("\nAn error has occured during the experiment, the experiment will now end.") + print( + "\nAn error has occured during the experiment, the experiment will now end." + ) db[experiment_id]["error_log"] = traceback.format_exc() finally: db[experiment_id]["final_behavior_tree"] = serialize_tree(origin_tree) db[experiment_id]["end_date"] = datetime.now().isoformat() return db - def main(): print("AIT Prototype #1 Simulator") - - #TODO: write up some context, assumptions made in the README + # TODO: write up some context, assumptions made in the README - #TODO: user query for files + # TODO: user query for files DB_FILE = "db.json" db = load_db(DB_FILE) - #load tree to run experiment on, and behavior library - + # load tree to run experiment on, and behavior library + RESOURCES_FILE = "resources.json" original_tree, behavior_library, context_paragraph = load_resources(RESOURCES_FILE) print(f"\nContext of this experiment: {context_paragraph}") - participant_id, experiment_id = experiment_setup(db, original_tree) db = run_experiment(db, original_tree, experiment_id, behavior_library) save_db(db, DB_FILE) - #TODO: define export file, that will be where we export the results to - + # TODO: define export file, that will be where we export the results to print("\nSimulation has ended.") - #TODO: visualize the differences between old and new behavior trees after experiment. - # Potentially use git diff + # TODO: visualize the differences between old and new behavior trees after experiment. + # Potentially use git diff + if __name__ == "__main__": - main() \ No newline at end of file + main()