From 0310cf7ec403ed44cb0f0013fa67434cea7b6acb Mon Sep 17 00:00:00 2001 From: yilinxia Date: Wed, 17 Apr 2024 13:42:58 -0500 Subject: [PATCH 01/10] blunder edge color --- .../functions/graph_data_functions/get_color.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_color.py b/src/py_arg_visualisation/functions/graph_data_functions/get_color.py index 7188e94..2ebb082 100644 --- a/src/py_arg_visualisation/functions/graph_data_functions/get_color.py +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_color.py @@ -1,7 +1,7 @@ def get_color(required_color: str, color_blind_mode: bool) -> str: if color_blind_mode: if required_color == 'gray': - return '#dddddd' + return '#888888' if required_color == 'blue': return '#6D90E3' if required_color == 'green': From 16167ed6ee8bb66105c08332b6187df82e67ea1d Mon Sep 17 00:00:00 2001 From: yilinxia Date: Wed, 24 Apr 2024 14:13:40 -0500 Subject: [PATCH 02/10] beautify the layout --- .../pages/21_visualise_abstract.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/py_arg_visualisation/pages/21_visualise_abstract.py b/src/py_arg_visualisation/pages/21_visualise_abstract.py index 93aa34c..b25e8de 100644 --- a/src/py_arg_visualisation/pages/21_visualise_abstract.py +++ b/src/py_arg_visualisation/pages/21_visualise_abstract.py @@ -180,7 +180,7 @@ def get_abstract_explanation_div(): dcc.Tab(label='Default visualisation', children=[ visdcc.Network(data={'nodes': [], 'edges': []}, id='abstract-argumentation-graph', - options={'height': '500px'})]), + options={'height': '545px'},)]), dcc.Tab(label='Layered visualisation', children=[ dbc.Row([ dbc.Col(html.B('Layout')), @@ -194,14 +194,18 @@ def get_abstract_explanation_div(): ], value='BT', id='21-abstract-graph-layout', - ) + ), ) ]), html.Div([ dash_interactive_graphviz.DashInteractiveGraphviz( id='explanation-graph', - style={'height': '500px'} - )], style={'height': '500px'}), + style={'height': '500px', + 'max-width': '98%', + 'overflow': 'hidden'} + )], style={'height': '510px', + 'max-width': '98%', + 'overflow': 'hidden'}), ]), ]))])]) From f94a4a7cf625e9cb79cf32ec57b014abd8e75d57 Mon Sep 17 00:00:00 2001 From: yilinxia Date: Wed, 24 Apr 2024 17:09:35 -0500 Subject: [PATCH 03/10] add against wind --- .../graph_data_functions/get_af_dot_string.py | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py index 05a906c..e5c1d72 100644 --- a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py @@ -89,14 +89,24 @@ def generate_dot_string( attack.to_argument.name] from_argument_number = \ number_by_argument[attack.from_argument.name] - - constraint = False + to_argument_number = \ + number_by_argument[attack.to_argument.name] + + against_wind = False style = 'solid' - try: - num = int(from_argument_number) - label = str( num + 1) - except ValueError: - label = from_argument_number + + from_num = float('inf') if from_argument_number == "∞" else int(from_argument_number) + to_num = float('inf') if to_argument_number == "∞" else int(to_argument_number) + against_wind = from_num == float('inf') and to_num != float('inf') + against_wind = against_wind or (from_num > to_num) + + if from_num == float('inf'): + label = '∞' + else: + label = str( from_num + 1) + + print(attack.from_argument.name, attack.to_argument.name,from_argument_number, to_argument_number, against_wind) + if from_argument_grounded_state == 'accepted' and \ to_argument_grounded_state == 'defeated': full_color = get_color('green', color_blind_mode) @@ -105,7 +115,6 @@ def generate_dot_string( full_color = get_color('red', color_blind_mode) elif from_argument_grounded_state != 'accepted' and \ to_argument_grounded_state == 'defeated': - constraint = True full_color = get_color('gray', color_blind_mode) style = 'dotted' label = '' @@ -126,23 +135,25 @@ def generate_dot_string( to_argument_extension_state == 'undefined': full_color = get_color('dark-yellow', color_blind_mode) else: - constraint = True extension_edge_color = get_color('gray', color_blind_mode) full_color = \ f'{extension_edge_color}' style = 'dotted' label = '' - if constraint: - constraint_str = "constraint=true" + if against_wind: + edge = f'"{attack.to_argument.name}" -> ' \ + f'"{attack.from_argument.name}" ' \ + f'[dir=back ' \ + f'color="{full_color}" ' \ + f'style="{style}" ' \ + f'taillabel="{label}"]\n' else: - constraint_str = '' - - edge = f'"{attack.from_argument.name}" -> ' \ - f'"{attack.to_argument.name}" ' \ - f'[color="{full_color}" ' \ - f'style="{style}" ' \ - f'taillabel="{label}" {constraint_str}]\n' + edge = f'"{attack.from_argument.name}" -> ' \ + f'"{attack.to_argument.name}" ' \ + f'[color="{full_color}" ' \ + f'style="{style}" ' \ + f'taillabel="{label}"]\n' else: edge = f'"{attack.from_argument.name}" -> ' \ f'"{attack.to_argument.name}"\n' @@ -150,13 +161,14 @@ def generate_dot_string( number_by_argument = {k: v for k, v in number_by_argument.items() if v != '∞'} + min_state_nodes = [node for node, value in number_by_argument.items() if value == min(number_by_argument.values())] max_state_nodes = [node for node, value in number_by_argument.items() if value == max(number_by_argument.values())] - min_rank_string = f"{{rank = min {' '.join(min_state_nodes)}}}" max_rank_string = f"{{rank = max {' '.join(max_state_nodes+undefined_arguments)}}}" dot_string += f" {min_rank_string}\n {max_rank_string}\n" dot_string += "}" + print(dot_string) return dot_string From 83bd9adbeba14c861b278a304f13ab2ed0670893 Mon Sep 17 00:00:00 2001 From: Yilin Xia Date: Wed, 24 Apr 2024 18:15:28 -0500 Subject: [PATCH 04/10] add ranks --- .../graph_data_functions/get_af_dot_string.py | 55 +++++++++++++------ .../pages/21_visualise_abstract.py | 19 ++++++- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py index e5c1d72..d38c660 100644 --- a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py @@ -1,5 +1,5 @@ import pathlib - +from collections import defaultdict import clingo from py_arg_visualisation.functions.graph_data_functions.get_color import \ @@ -26,7 +26,10 @@ def generate_plain_dot_string(argumentation_framework, layout=any): def generate_dot_string( - argumentation_framework, selected_arguments, color_blind_mode=False, layout=any): + argumentation_framework, selected_arguments, + color_blind_mode=False, + layout=any, + rank=any): gr_status_by_arg, number_by_argument = get_numbered_grounded_extension( argumentation_framework) dot_string = "digraph {\n" @@ -35,7 +38,7 @@ def generate_dot_string( # Adding node information is_extension_representation = False argument_extension_state = {} - undefined_arguments = [] + # undefined_arguments = [] unselected_arguments = \ {arg.name for arg in argumentation_framework.arguments} for color, arguments in selected_arguments.items(): @@ -47,8 +50,8 @@ def generate_dot_string( status = 'defeated' elif color == 'yellow': status = 'undefined' - if len(arguments)!=0: - undefined_arguments=arguments + # if len(arguments)!=0: + # undefined_arguments=arguments else: status = 'other' for argument_name in arguments: @@ -105,8 +108,8 @@ def generate_dot_string( else: label = str( from_num + 1) - print(attack.from_argument.name, attack.to_argument.name,from_argument_number, to_argument_number, against_wind) - + # print(attack.from_argument.name, attack.to_argument.name,from_argument_number, to_argument_number, against_wind) + arrow_style = 'vee' if from_argument_grounded_state == 'accepted' and \ to_argument_grounded_state == 'defeated': full_color = get_color('green', color_blind_mode) @@ -117,9 +120,9 @@ def generate_dot_string( to_argument_grounded_state == 'defeated': full_color = get_color('gray', color_blind_mode) style = 'dotted' + arrow_style = 'onormal' label = '' else: - # grounded_edge_color = get_color('yellow', color_blind_mode) if from_argument_extension_state == 'accepted' and \ to_argument_extension_state == 'defeated': @@ -139,20 +142,29 @@ def generate_dot_string( full_color = \ f'{extension_edge_color}' style = 'dotted' + arrow_style = 'onormal' label = '' if against_wind: + if style != 'dotted': + style = 'dashed' edge = f'"{attack.to_argument.name}" -> ' \ f'"{attack.from_argument.name}" ' \ f'[dir=back ' \ f'color="{full_color}" ' \ - f'style="{style}" ' \ - f'taillabel="{label}"]\n' + f'style= "{style}"' \ + f'fontcolor="{full_color}"' \ + f'arrowtail="{arrow_style}"' \ + f'arrowhead="{arrow_style}"' \ + f'headlabel="{label}"]\n' else: edge = f'"{attack.from_argument.name}" -> ' \ f'"{attack.to_argument.name}" ' \ f'[color="{full_color}" ' \ - f'style="{style}" ' \ + f'style="{style}"' \ + f'fontcolor="{full_color}"' \ + f'arrowtail="{arrow_style}"' \ + f'arrowhead="{arrow_style}"' \ f'taillabel="{label}"]\n' else: edge = f'"{attack.from_argument.name}" -> ' \ @@ -161,12 +173,23 @@ def generate_dot_string( number_by_argument = {k: v for k, v in number_by_argument.items() if v != '∞'} + if rank=="NR": + pass + elif rank=="AR": + max_value = max(number_by_argument.values()) + # Create a new dictionary excluding nodes with the maximum value + filtered_arguments = {k: v for k, v in number_by_argument.items() if v != max_value} + nodes_by_value = defaultdict(list) + for node, value in filtered_arguments.items(): + nodes_by_value[value].append(node) + for value, nodes in nodes_by_value.items(): + same_rank_string = f"{{rank = same {' '.join(nodes)}}}" + dot_string += f" {same_rank_string}\n" + elif rank=="MR": + min_state_nodes = [node for node, value in number_by_argument.items() if value == min(number_by_argument.values())] + min_rank_string = f"{{rank = min {' '.join(min_state_nodes)}}}" + dot_string += f" {min_rank_string}\n" - min_state_nodes = [node for node, value in number_by_argument.items() if value == min(number_by_argument.values())] - max_state_nodes = [node for node, value in number_by_argument.items() if value == max(number_by_argument.values())] - min_rank_string = f"{{rank = min {' '.join(min_state_nodes)}}}" - max_rank_string = f"{{rank = max {' '.join(max_state_nodes+undefined_arguments)}}}" - dot_string += f" {min_rank_string}\n {max_rank_string}\n" dot_string += "}" print(dot_string) return dot_string diff --git a/src/py_arg_visualisation/pages/21_visualise_abstract.py b/src/py_arg_visualisation/pages/21_visualise_abstract.py index b25e8de..826ab07 100644 --- a/src/py_arg_visualisation/pages/21_visualise_abstract.py +++ b/src/py_arg_visualisation/pages/21_visualise_abstract.py @@ -197,6 +197,20 @@ def get_abstract_explanation_div(): ), ) ]), + dbc.Row([ + dbc.Col(html.B('Rank')), + dbc.Col( + dbc.Select( + options=[ + {'label': 'No Rank', 'value': 'NR'}, + {'label': 'Min Rank', 'value': 'MR'}, + {'label': 'All Rank', 'value': 'AR'}, + ], + value='NR', + id='21-abstract-graph-rank', + ), + ) + ]), html.Div([ dash_interactive_graphviz.DashInteractiveGraphviz( id='explanation-graph', @@ -274,12 +288,13 @@ def generate_abstract_argumentation_framework( Input('selected-argument-store-abstract', 'data'), Input('color-blind-mode', 'on'), Input('21-abstract-graph-layout', 'value'), + Input('21-abstract-graph-rank', 'value'), Input('abstract-evaluation-accordion', 'active_item'), prevent_initial_call=True ) def create_abstract_argumentation_framework( arguments: str, attacks: str, selected_arguments: Dict[str, List[str]], - color_blind_mode: bool, dot_layout: str, active_item: str): + color_blind_mode: bool, dot_layout: str, dot_rank:str, active_item: str): """ Send the AF data to the graph for plotting. """ @@ -303,7 +318,7 @@ def create_abstract_argumentation_framework( if selected_arguments: dot_source = generate_dot_string( - arg_framework, selected_arguments, color_blind_mode, dot_layout) + arg_framework, selected_arguments, color_blind_mode, dot_layout, dot_rank) else: dot_source = generate_plain_dot_string(arg_framework, dot_layout) From 5e6d4d784c2d709d8c1a232cf42995ca95fb1844 Mon Sep 17 00:00:00 2001 From: yilinxia Date: Thu, 25 Apr 2024 10:35:52 -0500 Subject: [PATCH 05/10] fix edges --- .../graph_data_functions/get_af_dot_string.py | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py index d38c660..ef9940a 100644 --- a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py @@ -80,6 +80,8 @@ def generate_dot_string( # Adding edge information dot_string += f' edge[labeldistance=1.5 fontsize=12]\n' + print(gr_status_by_arg) + print(argument_extension_state) for attack in argumentation_framework.defeats: if is_extension_representation: from_argument_grounded_state = gr_status_by_arg[ @@ -95,9 +97,10 @@ def generate_dot_string( to_argument_number = \ number_by_argument[attack.to_argument.name] - against_wind = False - style = 'solid' + + # cal the against wind + against_wind = False from_num = float('inf') if from_argument_number == "∞" else int(from_argument_number) to_num = float('inf') if to_argument_number == "∞" else int(to_argument_number) against_wind = from_num == float('inf') and to_num != float('inf') @@ -108,39 +111,58 @@ def generate_dot_string( else: label = str( from_num + 1) - # print(attack.from_argument.name, attack.to_argument.name,from_argument_number, to_argument_number, against_wind) + + # set initial style + style = 'solid' arrow_style = 'vee' + # handle grounded extensions + # Accepted -> Defeated if from_argument_grounded_state == 'accepted' and \ to_argument_grounded_state == 'defeated': full_color = get_color('green', color_blind_mode) + # Defeated -> Accepted elif from_argument_grounded_state == 'defeated' and \ to_argument_grounded_state == 'accepted': full_color = get_color('red', color_blind_mode) - elif from_argument_grounded_state != 'accepted' and \ - to_argument_grounded_state == 'defeated': - full_color = get_color('gray', color_blind_mode) - style = 'dotted' - arrow_style = 'onormal' - label = '' else: - # grounded_edge_color = get_color('yellow', color_blind_mode) + #handle the stable extensions + # Stable Accepted -> Defeated (Grounded Undefined) if from_argument_extension_state == 'accepted' and \ to_argument_extension_state == 'defeated': extension_edge_color = get_color('green', color_blind_mode) full_color = \ f'{extension_edge_color}' + label = '' + # Stable Defeated -> Accepted(Grounded Undefined) elif from_argument_extension_state == 'defeated' and \ to_argument_extension_state == 'accepted': extension_edge_color = get_color('red', color_blind_mode) full_color = \ f'{extension_edge_color}' + label = '' + # Undefined -> Undefined elif from_argument_extension_state == 'undefined' and \ to_argument_extension_state == 'undefined': full_color = get_color('dark-yellow', color_blind_mode) - else: - extension_edge_color = get_color('gray', color_blind_mode) - full_color = \ - f'{extension_edge_color}' + + # Undefined -> Defeated + elif from_argument_extension_state == 'undefined' and \ + to_argument_extension_state == 'defeated': + full_color = get_color('gray', color_blind_mode) + style = 'dotted' + arrow_style = 'onormal' + label = '' + # Defeated -> Undefined + elif from_argument_extension_state == 'defeated' and \ + to_argument_extension_state == 'undefined': + full_color = get_color('gray', color_blind_mode) + style = 'dotted' + arrow_style = 'onormal' + label = '' + # Defeated -> Defeated + elif from_argument_extension_state == 'defeated' and \ + from_argument_extension_state == 'defeated': + full_color = get_color('gray', color_blind_mode) style = 'dotted' arrow_style = 'onormal' label = '' @@ -148,6 +170,8 @@ def generate_dot_string( if against_wind: if style != 'dotted': style = 'dashed' + else: + style = 'dotted' edge = f'"{attack.to_argument.name}" -> ' \ f'"{attack.from_argument.name}" ' \ f'[dir=back ' \ @@ -171,7 +195,7 @@ def generate_dot_string( f'"{attack.to_argument.name}"\n' dot_string += " "+edge - + # Enable Ranks number_by_argument = {k: v for k, v in number_by_argument.items() if v != '∞'} if rank=="NR": pass @@ -191,7 +215,7 @@ def generate_dot_string( dot_string += f" {min_rank_string}\n" dot_string += "}" - print(dot_string) + # print(dot_string) return dot_string From ef815dde47b42e6d54346d64651d6b196d1526ac Mon Sep 17 00:00:00 2001 From: yilinxia Date: Thu, 25 Apr 2024 11:37:28 -0500 Subject: [PATCH 06/10] add edge rm feature --- .../graph_data_functions/get_af_dot_string.py | 31 ++++++++++++------- .../graph_data_functions/get_color.py | 2 +- .../pages/21_visualise_abstract.py | 23 ++++++++++++-- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py index ef9940a..ac5af23 100644 --- a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py @@ -29,7 +29,8 @@ def generate_dot_string( argumentation_framework, selected_arguments, color_blind_mode=False, layout=any, - rank=any): + rank=any, + dot_rm_edge=any): gr_status_by_arg, number_by_argument = get_numbered_grounded_extension( argumentation_framework) dot_string = "digraph {\n" @@ -80,8 +81,6 @@ def generate_dot_string( # Adding edge information dot_string += f' edge[labeldistance=1.5 fontsize=12]\n' - print(gr_status_by_arg) - print(argument_extension_state) for attack in argumentation_framework.defeats: if is_extension_representation: from_argument_grounded_state = gr_status_by_arg[ @@ -96,9 +95,7 @@ def generate_dot_string( number_by_argument[attack.from_argument.name] to_argument_number = \ number_by_argument[attack.to_argument.name] - - - + # cal the against wind against_wind = False from_num = float('inf') if from_argument_number == "∞" else int(from_argument_number) @@ -144,12 +141,13 @@ def generate_dot_string( elif from_argument_extension_state == 'undefined' and \ to_argument_extension_state == 'undefined': full_color = get_color('dark-yellow', color_blind_mode) - + style = set_style("UU", style, dot_rm_edge) # Undefined -> Defeated elif from_argument_extension_state == 'undefined' and \ to_argument_extension_state == 'defeated': full_color = get_color('gray', color_blind_mode) style = 'dotted' + style = set_style("UD", style, dot_rm_edge) arrow_style = 'onormal' label = '' # Defeated -> Undefined @@ -157,6 +155,7 @@ def generate_dot_string( to_argument_extension_state == 'undefined': full_color = get_color('gray', color_blind_mode) style = 'dotted' + style = set_style("DU", style, dot_rm_edge) arrow_style = 'onormal' label = '' # Defeated -> Defeated @@ -164,14 +163,18 @@ def generate_dot_string( from_argument_extension_state == 'defeated': full_color = get_color('gray', color_blind_mode) style = 'dotted' + style = set_style("DD", style, dot_rm_edge) arrow_style = 'onormal' label = '' if against_wind: - if style != 'dotted': - style = 'dashed' + if style == 'dotted': + pass + elif style == "invis": + pass else: - style = 'dotted' + style = 'dashed' + style = set_style("AW", style, dot_rm_edge) edge = f'"{attack.to_argument.name}" -> ' \ f'"{attack.from_argument.name}" ' \ f'[dir=back ' \ @@ -215,7 +218,6 @@ def generate_dot_string( dot_string += f" {min_rank_string}\n" dot_string += "}" - # print(dot_string) return dot_string @@ -253,3 +255,10 @@ def get_numbered_grounded_extension(argumentation_framework): number_by_argument[argument_name] = str(atom.arguments[2]) status_by_argument[argument_name] = atom.arguments[0].name return status_by_argument, number_by_argument + + +def set_style(keyword, style, rm_edge): + if rm_edge!= None and keyword in rm_edge: + return "invis" + else: + return style diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_color.py b/src/py_arg_visualisation/functions/graph_data_functions/get_color.py index 2ebb082..8715750 100644 --- a/src/py_arg_visualisation/functions/graph_data_functions/get_color.py +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_color.py @@ -1,7 +1,7 @@ def get_color(required_color: str, color_blind_mode: bool) -> str: if color_blind_mode: if required_color == 'gray': - return '#888888' + return '#919191' if required_color == 'blue': return '#6D90E3' if required_color == 'green': diff --git a/src/py_arg_visualisation/pages/21_visualise_abstract.py b/src/py_arg_visualisation/pages/21_visualise_abstract.py index 826ab07..6d67f19 100644 --- a/src/py_arg_visualisation/pages/21_visualise_abstract.py +++ b/src/py_arg_visualisation/pages/21_visualise_abstract.py @@ -211,6 +211,23 @@ def get_abstract_explanation_div(): ), ) ]), + + dbc.Row([ + dbc.Col(html.B('Remove Edges')), + dbc.Col( + dcc.Dropdown( + options=[ + {'label': 'Defeated ⟶ Defeated', 'value': 'DD'}, + {'label': 'Undecided ⟶ Defeated', 'value': 'UD'}, + {'label': 'Defeated ⟶ Undecided', 'value': 'DU'}, + {'label': 'Undecided ⟶ Undecided', 'value': 'UU'}, + {'label': 'Against the Wind', 'value': 'AW'}, + ], + multi=True, + id='21-abstract-graph-edge-rm', + ) + ) + ]), html.Div([ dash_interactive_graphviz.DashInteractiveGraphviz( id='explanation-graph', @@ -289,12 +306,13 @@ def generate_abstract_argumentation_framework( Input('color-blind-mode', 'on'), Input('21-abstract-graph-layout', 'value'), Input('21-abstract-graph-rank', 'value'), + Input('21-abstract-graph-edge-rm', 'value'), Input('abstract-evaluation-accordion', 'active_item'), prevent_initial_call=True ) def create_abstract_argumentation_framework( arguments: str, attacks: str, selected_arguments: Dict[str, List[str]], - color_blind_mode: bool, dot_layout: str, dot_rank:str, active_item: str): + color_blind_mode: bool, dot_layout: str, dot_rank:str, dot_rm_edge: List[str], active_item: str): """ Send the AF data to the graph for plotting. """ @@ -315,10 +333,9 @@ def create_abstract_argumentation_framework( data = get_argumentation_framework_graph_data( arg_framework, selected_arguments, color_blind_mode) - if selected_arguments: dot_source = generate_dot_string( - arg_framework, selected_arguments, color_blind_mode, dot_layout, dot_rank) + arg_framework, selected_arguments, color_blind_mode, dot_layout, dot_rank, dot_rm_edge) else: dot_source = generate_plain_dot_string(arg_framework, dot_layout) From 43871751f869f7ed12bf2429d367fabf2cf10870 Mon Sep 17 00:00:00 2001 From: yilinxia Date: Thu, 25 Apr 2024 12:03:02 -0500 Subject: [PATCH 07/10] add con --- .../graph_data_functions/get_af_dot_string.py | 15 ++++++++++++ .../pages/21_visualise_abstract.py | 23 ++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py index ac5af23..12f6682 100644 --- a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py @@ -30,6 +30,7 @@ def generate_dot_string( color_blind_mode=False, layout=any, rank=any, + dot_con=any, dot_rm_edge=any): gr_status_by_arg, number_by_argument = get_numbered_grounded_extension( argumentation_framework) @@ -112,6 +113,7 @@ def generate_dot_string( # set initial style style = 'solid' arrow_style = 'vee' + constraint_value = '' # handle grounded extensions # Accepted -> Defeated if from_argument_grounded_state == 'accepted' and \ @@ -142,6 +144,7 @@ def generate_dot_string( to_argument_extension_state == 'undefined': full_color = get_color('dark-yellow', color_blind_mode) style = set_style("UU", style, dot_rm_edge) + constraint_value = set_con("UU", dot_con) # Undefined -> Defeated elif from_argument_extension_state == 'undefined' and \ to_argument_extension_state == 'defeated': @@ -149,6 +152,7 @@ def generate_dot_string( style = 'dotted' style = set_style("UD", style, dot_rm_edge) arrow_style = 'onormal' + constraint_value= set_con("UD", dot_con) label = '' # Defeated -> Undefined elif from_argument_extension_state == 'defeated' and \ @@ -157,6 +161,7 @@ def generate_dot_string( style = 'dotted' style = set_style("DU", style, dot_rm_edge) arrow_style = 'onormal' + constraint_value = set_con("DU", dot_con) label = '' # Defeated -> Defeated elif from_argument_extension_state == 'defeated' and \ @@ -165,6 +170,7 @@ def generate_dot_string( style = 'dotted' style = set_style("DD", style, dot_rm_edge) arrow_style = 'onormal' + constraint_value = set_con("DD", dot_con) label = '' if against_wind: @@ -180,6 +186,7 @@ def generate_dot_string( f'[dir=back ' \ f'color="{full_color}" ' \ f'style= "{style}"' \ + f'{constraint_value}' \ f'fontcolor="{full_color}"' \ f'arrowtail="{arrow_style}"' \ f'arrowhead="{arrow_style}"' \ @@ -189,6 +196,7 @@ def generate_dot_string( f'"{attack.to_argument.name}" ' \ f'[color="{full_color}" ' \ f'style="{style}"' \ + f'{constraint_value}' \ f'fontcolor="{full_color}"' \ f'arrowtail="{arrow_style}"' \ f'arrowhead="{arrow_style}"' \ @@ -218,6 +226,7 @@ def generate_dot_string( dot_string += f" {min_rank_string}\n" dot_string += "}" + print(dot_string) return dot_string @@ -262,3 +271,9 @@ def set_style(keyword, style, rm_edge): return "invis" else: return style + +def set_con(keyword, rm_edge): + if rm_edge!= None and keyword in rm_edge: + return 'constraint="false"' + else: + return '' \ No newline at end of file diff --git a/src/py_arg_visualisation/pages/21_visualise_abstract.py b/src/py_arg_visualisation/pages/21_visualise_abstract.py index 6d67f19..bb9017b 100644 --- a/src/py_arg_visualisation/pages/21_visualise_abstract.py +++ b/src/py_arg_visualisation/pages/21_visualise_abstract.py @@ -211,7 +211,22 @@ def get_abstract_explanation_div(): ), ) ]), - + dbc.Row([ + dbc.Col(html.B('Edge Constraint (False)')), + dbc.Col( + dcc.Dropdown( + options=[ + {'label': 'Defeated ⟶ Defeated', 'value': 'DD'}, + {'label': 'Undecided ⟶ Defeated', 'value': 'UD'}, + {'label': 'Defeated ⟶ Undecided', 'value': 'DU'}, + {'label': 'Undecided ⟶ Undecided', 'value': 'UU'}, + {'label': 'Against the Wind', 'value': 'AW'}, + ], + multi=True, + id='21-abstract-graph-edge-con', + ) + ) + ]), dbc.Row([ dbc.Col(html.B('Remove Edges')), dbc.Col( @@ -306,13 +321,15 @@ def generate_abstract_argumentation_framework( Input('color-blind-mode', 'on'), Input('21-abstract-graph-layout', 'value'), Input('21-abstract-graph-rank', 'value'), + Input('21-abstract-graph-edge-con', 'value'), Input('21-abstract-graph-edge-rm', 'value'), Input('abstract-evaluation-accordion', 'active_item'), prevent_initial_call=True ) def create_abstract_argumentation_framework( arguments: str, attacks: str, selected_arguments: Dict[str, List[str]], - color_blind_mode: bool, dot_layout: str, dot_rank:str, dot_rm_edge: List[str], active_item: str): + color_blind_mode: bool, dot_layout: str, dot_rank:str, dot_con:List[str], + dot_rm_edge: List[str], active_item: str): """ Send the AF data to the graph for plotting. """ @@ -335,7 +352,7 @@ def create_abstract_argumentation_framework( arg_framework, selected_arguments, color_blind_mode) if selected_arguments: dot_source = generate_dot_string( - arg_framework, selected_arguments, color_blind_mode, dot_layout, dot_rank, dot_rm_edge) + arg_framework, selected_arguments, color_blind_mode, dot_layout, dot_rank, dot_con, dot_rm_edge) else: dot_source = generate_plain_dot_string(arg_framework, dot_layout) From 008e60568387bc7572afdcf7ccf4535393ec6b60 Mon Sep 17 00:00:00 2001 From: yilinxia Date: Thu, 25 Apr 2024 12:49:23 -0500 Subject: [PATCH 08/10] add dot download button --- .../graph_data_functions/get_af_dot_string.py | 2 +- .../pages/21_visualise_abstract.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py index 12f6682..03be198 100644 --- a/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_af_dot_string.py @@ -226,7 +226,7 @@ def generate_dot_string( dot_string += f" {min_rank_string}\n" dot_string += "}" - print(dot_string) + # print(dot_string) return dot_string diff --git a/src/py_arg_visualisation/pages/21_visualise_abstract.py b/src/py_arg_visualisation/pages/21_visualise_abstract.py index bb9017b..860bb39 100644 --- a/src/py_arg_visualisation/pages/21_visualise_abstract.py +++ b/src/py_arg_visualisation/pages/21_visualise_abstract.py @@ -244,12 +244,14 @@ def get_abstract_explanation_div(): ) ]), html.Div([ + dbc.Button('Download dot', id='21-dot-download-button'), + dcc.Download(id="21-dot-download"), dash_interactive_graphviz.DashInteractiveGraphviz( id='explanation-graph', style={'height': '500px', 'max-width': '98%', 'overflow': 'hidden'} - )], style={'height': '510px', + )], style={'height': '550px', 'max-width': '98%', 'overflow': 'hidden'}), ]), @@ -313,8 +315,10 @@ def generate_abstract_argumentation_framework( @callback( + Output('21-dot-download', 'data'), Output('abstract-argumentation-graph', 'data'), Output('explanation-graph', 'dot_source'), + Input('21-dot-download-button', 'n_clicks'), Input('abstract-arguments', 'value'), Input('abstract-attacks', 'value'), Input('selected-argument-store-abstract', 'data'), @@ -327,7 +331,7 @@ def generate_abstract_argumentation_framework( prevent_initial_call=True ) def create_abstract_argumentation_framework( - arguments: str, attacks: str, selected_arguments: Dict[str, List[str]], + _nr_clicks: int, arguments: str, attacks: str, selected_arguments: Dict[str, List[str]], color_blind_mode: bool, dot_layout: str, dot_rank:str, dot_con:List[str], dot_rm_edge: List[str], active_item: str): """ @@ -355,8 +359,12 @@ def create_abstract_argumentation_framework( arg_framework, selected_arguments, color_blind_mode, dot_layout, dot_rank, dot_con, dot_rm_edge) else: dot_source = generate_plain_dot_string(arg_framework, dot_layout) - - return data, dot_source + + changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0] + if "21-dot-download-button" in changed_id: + return dict(content=dot_source, filename="pyarg_output.dot"), data, dot_source + else: + return None, data, dot_source @callback( From 1b171df3eac76b2edc4b8eb1123d173f0f7c835b Mon Sep 17 00:00:00 2001 From: Yilin Xia Date: Thu, 25 Apr 2024 23:55:08 -0500 Subject: [PATCH 09/10] adjust the structure --- .../pages/21_visualise_abstract.py | 165 ++++++++++-------- 1 file changed, 96 insertions(+), 69 deletions(-) diff --git a/src/py_arg_visualisation/pages/21_visualise_abstract.py b/src/py_arg_visualisation/pages/21_visualise_abstract.py index 860bb39..ebdf7b5 100644 --- a/src/py_arg_visualisation/pages/21_visualise_abstract.py +++ b/src/py_arg_visualisation/pages/21_visualise_abstract.py @@ -178,84 +178,111 @@ def get_abstract_explanation_div(): dbc.Card( dcc.Tabs([ dcc.Tab(label='Default visualisation', children=[ - visdcc.Network(data={'nodes': [], 'edges': []}, - id='abstract-argumentation-graph', - options={'height': '545px'},)]), + visdcc.Network( + data={'nodes': [], 'edges': []}, + id='abstract-argumentation-graph', + options={'height': '545px'} + ), + ]), dcc.Tab(label='Layered visualisation', children=[ + html.Div(style={'height': '5px'}), dbc.Row([ - dbc.Col(html.B('Layout')), - dbc.Col( - dbc.Select( - options=[ - {'label': 'Left to right', 'value': 'LR'}, - {'label': 'Right to left', 'value': 'RL'}, - {'label': 'Bottom to top', 'value': 'BT'}, - {'label': 'Top to bottom', 'value': 'TB'} - ], - value='BT', - id='21-abstract-graph-layout', - ), - ) - ]), - dbc.Row([ - dbc.Col(html.B('Rank')), - dbc.Col( - dbc.Select( - options=[ - {'label': 'No Rank', 'value': 'NR'}, - {'label': 'Min Rank', 'value': 'MR'}, - {'label': 'All Rank', 'value': 'AR'}, - ], - value='NR', - id='21-abstract-graph-rank', - ), - ) - ]), - dbc.Row([ - dbc.Col(html.B('Edge Constraint (False)')), dbc.Col( - dcc.Dropdown( - options=[ - {'label': 'Defeated ⟶ Defeated', 'value': 'DD'}, - {'label': 'Undecided ⟶ Defeated', 'value': 'UD'}, - {'label': 'Defeated ⟶ Undecided', 'value': 'DU'}, - {'label': 'Undecided ⟶ Undecided', 'value': 'UU'}, - {'label': 'Against the Wind', 'value': 'AW'}, - ], - multi=True, - id='21-abstract-graph-edge-con', - ) - ) - ]), - dbc.Row([ - dbc.Col(html.B('Remove Edges')), + [ + dbc.Row([ + dbc.Col(html.B('Layout'), width=5), + dbc.Col( + dbc.Select( + options=[ + {'label': 'Left to right', 'value': 'LR'}, + {'label': 'Right to left', 'value': 'RL'}, + {'label': 'Bottom to top', 'value': 'BT'}, + {'label': 'Top to bottom', 'value': 'TB'} + ], + value='BT', + id='21-abstract-graph-layout', + ), + ), + ]), + html.Div(style={'height': '5px'}), + dbc.Row([ + dbc.Col(html.B('Rank'), width=5), + dbc.Col( + dbc.Select( + options=[ + {'label': 'No Rank', 'value': 'NR'}, + {'label': 'Min Rank', 'value': 'MR'}, + {'label': 'All Rank', 'value': 'AR'}, + ], + value='NR', + id='21-abstract-graph-rank', + ), + ), + ]), + html.Div(style={'height': '5px'}), + dbc.Row([ + dbc.Col(html.B('Edge Constraint (False)'), width=5), + dbc.Col( + dcc.Dropdown( + options=[ + {'label': 'Defeated ⟶ Defeated', 'value': 'DD'}, + {'label': 'Undecided ⟶ Defeated', 'value': 'UD'}, + {'label': 'Defeated ⟶ Undecided', 'value': 'DU'}, + {'label': 'Undecided ⟶ Undecided', 'value': 'UU'}, + {'label': 'Against the Wind', 'value': 'AW'}, + ], + multi=True, + id='21-abstract-graph-edge-con', + ), + ), + ]), + html.Div(style={'height': '5px'}), + dbc.Row([ + dbc.Col(html.B('Remove Edges'), width=5), + dbc.Col( + dcc.Dropdown( + options=[ + {'label': 'Defeated ⟶ Defeated', 'value': 'DD'}, + {'label': 'Undecided ⟶ Defeated', 'value': 'UD'}, + {'label': 'Defeated ⟶ Undecided', 'value': 'DU'}, + {'label': 'Undecided ⟶ Undecided', 'value': 'UU'}, + {'label': 'Against the Wind', 'value': 'AW'}, + ], + multi=True, + id='21-abstract-graph-edge-rm', + ), + ), + ]), + ], + width=9, + ), dbc.Col( - dcc.Dropdown( - options=[ - {'label': 'Defeated ⟶ Defeated', 'value': 'DD'}, - {'label': 'Undecided ⟶ Defeated', 'value': 'UD'}, - {'label': 'Defeated ⟶ Undecided', 'value': 'DU'}, - {'label': 'Undecided ⟶ Undecided', 'value': 'UU'}, - {'label': 'Against the Wind', 'value': 'AW'}, - ], - multi=True, - id='21-abstract-graph-edge-rm', - ) - ) + [ + dbc.Button( + 'Download dot', + id='21-dot-download-button', + style={ + 'width': '95px', + 'margin': '10px auto', + }, + ), + dcc.Download(id="21-dot-download"), + ], + className="d-flex justify-content-center align-items-center", + width=3, + ), ]), html.Div([ - dbc.Button('Download dot', id='21-dot-download-button'), - dcc.Download(id="21-dot-download"), dash_interactive_graphviz.DashInteractiveGraphviz( id='explanation-graph', - style={'height': '500px', - 'max-width': '98%', - 'overflow': 'hidden'} - )], style={'height': '550px', - 'max-width': '98%', - 'overflow': 'hidden'}), + style={'height': '550px', 'max-width': '98%', 'overflow': 'hidden'}, + ), + ], style={'height': '550px', 'max-width': '98%', 'overflow': 'hidden'}), ]), - ]))])]) + ]), + ), + ]), +]) layout_abstract = dbc.Row([left_column, right_column]) layout = html.Div([html.H1( From 555eac90578fb11dd9b7af2c9857763e04d8fca5 Mon Sep 17 00:00:00 2001 From: Yilin Xia Date: Wed, 19 Jun 2024 20:13:54 -0500 Subject: [PATCH 10/10] adjust engine --- src/py_arg_visualisation/pages/21_visualise_abstract.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/py_arg_visualisation/pages/21_visualise_abstract.py b/src/py_arg_visualisation/pages/21_visualise_abstract.py index ebdf7b5..206ca17 100644 --- a/src/py_arg_visualisation/pages/21_visualise_abstract.py +++ b/src/py_arg_visualisation/pages/21_visualise_abstract.py @@ -275,6 +275,7 @@ def get_abstract_explanation_div(): html.Div([ dash_interactive_graphviz.DashInteractiveGraphviz( id='explanation-graph', + persisted_props={'engine': "neato"}, style={'height': '550px', 'max-width': '98%', 'overflow': 'hidden'}, ), ], style={'height': '550px', 'max-width': '98%', 'overflow': 'hidden'}),