From 2f7d72f3e2fe0b4b4d7ed5f75df24802f71ff9eb Mon Sep 17 00:00:00 2001 From: Eimear Maguire Date: Fri, 17 Oct 2025 14:33:58 +0100 Subject: [PATCH 1/3] Ensure I-nodes are only recognised as being introduced once --- src/xaif_toolbox.py | 50 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/xaif_toolbox.py b/src/xaif_toolbox.py index d6d0020..4fd8859 100644 --- a/src/xaif_toolbox.py +++ b/src/xaif_toolbox.py @@ -301,6 +301,7 @@ def path_to_start(l_node, all_nodes): # also adds locution associations to i-nodes def add_speakers(all_nodes, verbose=False): + print("HELLOOOOOO") said = {} for n in [n for n in all_nodes if all_nodes[n]['type'] == 'L']: @@ -332,10 +333,25 @@ def add_speakers(all_nodes, verbose=False): for ya_out in all_nodes[ya]['eout']: if all_nodes[ya_out]['type'] == 'I': if spkr != '': - all_nodes[ya_out]['saidby'].append(spkr) - said[spkr].append(all_nodes[ya_out]['nodeID']) - - all_nodes[ya_out]['introby'].append(all_nodes[n]['nodeID']) + if all_nodes[ya]['text'] != 'Disagreeing': + all_nodes[ya_out]['saidby'].append(spkr) + said[spkr].append(all_nodes[ya_out]['nodeID']) + + # If I-node has no known introduction, assume this is it + if len(all_nodes[ya_out]['introby']) == 0: + print(f"First finding of intro to {ya_out} is {all_nodes[n]['nodeID']}") + all_nodes[ya_out]['introby'].append(all_nodes[n]['nodeID']) + # Otherwise keep the earliest one + else: + print(f"Already found an intro to {ya_out}: {all_nodes[ya_out]['introby']}") + # Get ID of L-node associated as introducing this I-node + prev_intro = all_nodes[ya_out]['introby'][0] + # Keep whichever one is earlier chronologically + # i.e. if prev 'intro' is actually later than current L-node, replace it + if all_nodes[prev_intro]['chron'] > all_nodes[n]['chron']: + print(f"Prev 'intro' {prev_intro} is newer ({all_nodes[prev_intro]['chron']}) than current {n} ({all_nodes[n]['chron']})") + all_nodes[ya_out]['introby'] = [n] + print(f"--> intro for {ya_out} is now {all_nodes[ya_out]['introby']}") # Reported speech: I-node should be attributed to the quoting speaker @@ -376,18 +392,30 @@ def add_speakers(all_nodes, verbose=False): spkr = splits[0].strip() for ta_out in all_nodes[n]['eout']: - if all_nodes[ta_out]['type'] == 'YA': - for i_out in all_nodes[ta_out]['eout']: - if all_nodes[i_out]['type'] == 'I' and spkr != '': + if all_nodes[ta_out]['type'] == 'YA' and all_nodes[ta_out]['text'] != 'Disagreeing': + for ya_out in all_nodes[ta_out]['eout']: + if all_nodes[ya_out]['type'] == 'I' and spkr != '': # Record node-wise - all_nodes[i_out]['saidby'].append(spkr) - all_nodes[i_out]['introby'].append(l2) + all_nodes[ya_out]['saidby'].append(spkr) + + # If I-node has no known introduction, assume this is it + if len(all_nodes[ya_out]['introby']) == 0: + all_nodes[ya_out]['introby'].append(l2) + # Otherwise keep the earliest one + else: + prev_intro = all_nodes[ya_out]['introby'][0] + # if prev 'intro' is actually later than current L-node, replace it + if all_nodes[prev_intro]['chron'] > all_nodes[l2]['chron']: + # print(f"Prev 'intro' {prev_intro} is newer ({all_nodes[prev_intro]['chron']}) than current {n} ({all_nodes[n]['chron']})") + all_nodes[ya_out]['introby'] = [l2] + # print(f"--> intro for {ya_out} is now {all_nodes[ya_out]['introby']}") + # Record speaker-wise if spkr in said: - said[spkr].append(all_nodes[i_out]['nodeID']) + said[spkr].append(all_nodes[ya_out]['nodeID']) else: - said[spkr] = all_nodes[i_out]['nodeID'] + said[spkr] = all_nodes[ya_out]['nodeID'] # Adding speaker attribution to arg relations based on speaker of L-nodes descended from the anchoring TA From 3b46c2c7c385cfb1b3e2f12ad144cc518351b1fb Mon Sep 17 00:00:00 2001 From: Eimear Maguire Date: Fri, 17 Oct 2025 14:51:55 +0100 Subject: [PATCH 2/3] Correctly recognise agreement when anchored in TA --- src/xaif_toolbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xaif_toolbox.py b/src/xaif_toolbox.py index 4fd8859..0ecf500 100644 --- a/src/xaif_toolbox.py +++ b/src/xaif_toolbox.py @@ -501,7 +501,7 @@ def add_agreement(all_nodes): if all_nodes[n]['type'] == 'YA' and all_nodes[n]['text'] == ('Agreeing' or 'Asserting'): for e_in in all_nodes[n]['ein']: for e_out in all_nodes[n]['eout']: - if all_nodes[e_in]['type'] == 'L' and all_nodes[e_out]['type'] == 'I': + if (all_nodes[e_in]['type'] == 'L' or all_nodes[e_in]['type'] == 'TA') and all_nodes[e_out]['type'] == 'I': all_nodes[e_out]['agree'] = all_nodes[e_out]['agree'] + all_nodes[e_in]['speaker'] # all_nodes[e_out]['agree'].append(all_nodes[e_in]['speaker']) From c1eedc3ca15fb306784b621e065be0ddb91f60fb Mon Sep 17 00:00:00 2001 From: Eimear Maguire Date: Fri, 17 Oct 2025 16:03:36 +0100 Subject: [PATCH 3/3] Skip unattributed RAs when checking depths in speaker mode --- src/analytics.py | 20 +++++++++++++++++--- src/xaif_toolbox.py | 13 ++++++------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/analytics.py b/src/analytics.py index 9eb6b85..4eb4901 100644 --- a/src/analytics.py +++ b/src/analytics.py @@ -1415,14 +1415,22 @@ def path_lens_from_arg(xa_id, seen_xas, all_nodes, rel_type='RA', speaker=False, path_list = [] if verbose: - print("Targeting I-node(s)", i_node_concls) + print("Targeting conclusion I-node(s)", i_node_concls) for i_concl in i_node_concls: + if verbose: + print(f"\tCurrently on {i_concl}") if speaker: + if verbose: + print(f"\t\t{rel_type}-nodes with edge to {i_concl}: {[n for n in all_nodes if i_concl in all_nodes[n]['ein']]}") xas_out = [n for n in all_nodes if i_concl in all_nodes[n]['ein'] and all_nodes[n]['type'] == rel_type + and len(all_nodes[n]['speaker']) != 0 and all_nodes[n]['speaker'][0] == target_spkr] + else: + if verbose: + print(f"\t\t{rel_type}-nodes with edge to {i_concl}: {[n for n in all_nodes if i_concl in all_nodes[n]['ein']]}") xas_out = [n for n in all_nodes if i_concl in all_nodes[n]['ein'] and all_nodes[n]['type'] == rel_type] if verbose: @@ -1504,11 +1512,14 @@ def arg_depths(xaif, rel_type='RA', speaker=False, verbose=False, skip_altgive=T for xa in spkr_xa_all: spkr_starts += initial_arg(xa, [], all_nodes, speaker=speaker, verbose=verbose, rel_type=rel_type, skip_altgive=skip_altgive) spkr_starts = list(set(spkr_starts)) + + if verbose: + print(f"Starters for {spkr} are: ", spkr_starts) # Follow each argument path from the first RAs in each path for starter_arg in spkr_starts: if verbose: - print("Looking for argument ", starter_arg) + print("\n!!! New Start !!! Looking for argument ", starter_arg) new_path_lens = path_lens_from_arg(starter_arg, [], all_nodes, speaker=speaker, verbose=verbose) xa_depths[spkr][f'{depth_type}_depths'] = xa_depths[spkr][f'{depth_type}_depths'] + new_path_lens @@ -1524,10 +1535,13 @@ def arg_depths(xaif, rel_type='RA', speaker=False, verbose=False, skip_altgive=T starts += initial_arg(xa, [], all_nodes, speaker=speaker, verbose=verbose, rel_type=rel_type) starts = list(set(starts)) + if verbose: + print("Starters are: ", starts) + # Follow each argument path from the first RAs in each path for starter_arg in starts: if verbose: - print("Looking for argument ", starter_arg) + print("\n!!! New Start !!! Looking for argument ", starter_arg) new_path_lens = path_lens_from_arg(starter_arg, [], all_nodes, rel_type=rel_type, speaker=speaker, verbose=verbose) xa_depths[f'{depth_type}_depths'] = xa_depths[f'{depth_type}_depths'] + new_path_lens diff --git a/src/xaif_toolbox.py b/src/xaif_toolbox.py index 0ecf500..bc59807 100644 --- a/src/xaif_toolbox.py +++ b/src/xaif_toolbox.py @@ -301,7 +301,6 @@ def path_to_start(l_node, all_nodes): # also adds locution associations to i-nodes def add_speakers(all_nodes, verbose=False): - print("HELLOOOOOO") said = {} for n in [n for n in all_nodes if all_nodes[n]['type'] == 'L']: @@ -310,7 +309,7 @@ def add_speakers(all_nodes, verbose=False): splits = all_nodes[n]['text'].split(':') if len(splits) < 2: spkr = '' - print(f"L-node with no recognisable speaker:\t{all_nodes[n]['nodeID']}") + # print(f"L-node with no recognisable speaker:\t{all_nodes[n]['nodeID']}") else: spkr = splits[0].strip() @@ -339,19 +338,19 @@ def add_speakers(all_nodes, verbose=False): # If I-node has no known introduction, assume this is it if len(all_nodes[ya_out]['introby']) == 0: - print(f"First finding of intro to {ya_out} is {all_nodes[n]['nodeID']}") + # print(f"First finding of intro to {ya_out} is {all_nodes[n]['nodeID']}") all_nodes[ya_out]['introby'].append(all_nodes[n]['nodeID']) # Otherwise keep the earliest one else: - print(f"Already found an intro to {ya_out}: {all_nodes[ya_out]['introby']}") + # print(f"Already found an intro to {ya_out}: {all_nodes[ya_out]['introby']}") # Get ID of L-node associated as introducing this I-node prev_intro = all_nodes[ya_out]['introby'][0] # Keep whichever one is earlier chronologically # i.e. if prev 'intro' is actually later than current L-node, replace it if all_nodes[prev_intro]['chron'] > all_nodes[n]['chron']: - print(f"Prev 'intro' {prev_intro} is newer ({all_nodes[prev_intro]['chron']}) than current {n} ({all_nodes[n]['chron']})") + # print(f"Prev 'intro' {prev_intro} is newer ({all_nodes[prev_intro]['chron']}) than current {n} ({all_nodes[n]['chron']})") all_nodes[ya_out]['introby'] = [n] - print(f"--> intro for {ya_out} is now {all_nodes[ya_out]['introby']}") + # print(f"--> intro for {ya_out} is now {all_nodes[ya_out]['introby']}") # Reported speech: I-node should be attributed to the quoting speaker @@ -386,7 +385,7 @@ def add_speakers(all_nodes, verbose=False): splits = all_nodes[l2]['text'].split(':') if len(splits) < 2: - print(f"L-node with no recognisable speaker:\t{all_nodes[n]['nodeID']}") + # print(f"L-node with no recognisable speaker:\t{all_nodes[n]['nodeID']}") spkr = '' else: spkr = splits[0].strip()