From f9cf7e640c0fdf052bb7edeb2cf84def862a6207 Mon Sep 17 00:00:00 2001 From: ccedmendoza Date: Mon, 22 Jul 2019 13:37:58 +0200 Subject: [PATCH 1/9] Make filter work with file containing header --- tools/filters/gff/gtf_filter_by_attribute_values_list.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.py b/tools/filters/gff/gtf_filter_by_attribute_values_list.py index f1754f1bf3a..6097a1f191d 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.py +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.py @@ -55,10 +55,11 @@ def gff_filter(gff_file, attribute_name, ids_file, output_file): # Filter GFF file using ids. output = open(output_file, 'w') for line in open(gff_file): - fields = line.split('\t') - attributes = parse_gff_attributes(fields[8]) - if (attribute_name in attributes) and (attributes[attribute_name] in ids_dict): - output.write(line) + if (line[0] != '#'): + fields = line.split('\t') + attributes = parse_gff_attributes(fields[8]) + if (attribute_name in attributes) and (attributes[attribute_name] in ids_dict): + output.write(line) output.close() From f44c0b311c2b2f25b969ee8e01a6a3dd5698d152 Mon Sep 17 00:00:00 2001 From: ccedmendoza Date: Tue, 23 Jul 2019 12:52:27 +0200 Subject: [PATCH 2/9] Simplify script while adding empty lines and comments handling --- .../gtf_filter_by_attribute_values_list.py | 65 +++++-------------- 1 file changed, 17 insertions(+), 48 deletions(-) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.py b/tools/filters/gff/gtf_filter_by_attribute_values_list.py index 6097a1f191d..008fedf5908 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.py +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.py @@ -7,60 +7,29 @@ from __future__ import print_function import sys - - -def parse_gff_attributes(attr_str): - """ - Parses a GFF/GTF attribute string and returns a dictionary of name-value - pairs. The general format for a GFF3 attributes string is - name1=value1;name2=value2 - The general format for a GTF attribute string is - name1 "value1" ; name2 "value2" - The general format for a GFF attribute string is a single string that - denotes the interval's group; in this case, method returns a dictionary - with a single key-value pair, and key name is 'group' - """ - attributes_list = attr_str.split(";") - attributes = {} - for name_value_pair in attributes_list: - # Try splitting by space and, if necessary, by '=' sign. - pair = name_value_pair.strip().split(" ") - if len(pair) == 1: - pair = name_value_pair.strip().split("=") - if len(pair) == 1: - # Could not split for some reason -- raise exception? - continue - if pair == '': - continue - name = pair[0].strip() - if name == '': - continue - # Need to strip double quote from values - value = pair[1].strip(" \"") - attributes[name] = value - - if len(attributes) == 0: - # Could not split attributes string, so entire string must be - # 'group' attribute. This is the case for strictly GFF files. - attributes['group'] = attr_str - return attributes - +import re def gff_filter(gff_file, attribute_name, ids_file, output_file): # Put ids in dict for quick lookup. ids_dict = {} - for line in open(ids_file): - ids_dict[line.split('\t')[0].strip()] = True + with open(ids_file) as ids: + for line in ids: + ids_dict[line.split('\t')[0].strip()] = True # Filter GFF file using ids. - output = open(output_file, 'w') - for line in open(gff_file): - if (line[0] != '#'): - fields = line.split('\t') - attributes = parse_gff_attributes(fields[8]) - if (attribute_name in attributes) and (attributes[attribute_name] in ids_dict): - output.write(line) - output.close() + with open(output_file, 'w') as output: + with open(gff_file) as gff: + for line in gff: + # Keep commented lines + if line.startswith('#'): + output.write(line) + else: + # Create pattern using attribute to filter + prog = re.compile(r".*"+re.escape(attribute_name)+r" \"(.+?)\"\;") + line_match = prog.match(line) + # If there is a match and the id after the filtered attribute (attribut "id";) is in the list. We print it + if line_match and line_match.group(1) in ids_dict: + output.write(line) if __name__ == "__main__": From 8491730b834aa3fff3947d12b7d8cc27bdbcb549 Mon Sep 17 00:00:00 2001 From: ccedmendoza Date: Tue, 23 Jul 2019 13:25:52 +0200 Subject: [PATCH 3/9] Corrent linting --- .../gff/gtf_filter_by_attribute_values_list.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.py b/tools/filters/gff/gtf_filter_by_attribute_values_list.py index 008fedf5908..8ae704297bb 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.py +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.py @@ -6,8 +6,9 @@ # from __future__ import print_function -import sys import re +import sys + def gff_filter(gff_file, attribute_name, ids_file, output_file): # Put ids in dict for quick lookup. @@ -24,12 +25,12 @@ def gff_filter(gff_file, attribute_name, ids_file, output_file): if line.startswith('#'): output.write(line) else: - # Create pattern using attribute to filter - prog = re.compile(r".*"+re.escape(attribute_name)+r" \"(.+?)\"\;") + # Create pattern using attribute to filter + prog = re.compile(r".*" + re.escape(attribute_name) + r" \"(.+?)\"\;") line_match = prog.match(line) - # If there is a match and the id after the filtered attribute (attribut "id";) is in the list. We print it + # If there is a match and the id after the filtered attribute (attribut "id";) is in the list, we print it if line_match and line_match.group(1) in ids_dict: - output.write(line) + output.write(line) if __name__ == "__main__": From 765e70080383e4b5bc09e51d3cd4c268f96f09de Mon Sep 17 00:00:00 2001 From: ccedmendoza Date: Tue, 23 Jul 2019 13:35:31 +0200 Subject: [PATCH 4/9] remove trailing white space --- tools/filters/gff/gtf_filter_by_attribute_values_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.py b/tools/filters/gff/gtf_filter_by_attribute_values_list.py index 8ae704297bb..52db30dcd4c 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.py +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.py @@ -25,7 +25,7 @@ def gff_filter(gff_file, attribute_name, ids_file, output_file): if line.startswith('#'): output.write(line) else: - # Create pattern using attribute to filter + # Create pattern using attribute to filter prog = re.compile(r".*" + re.escape(attribute_name) + r" \"(.+?)\"\;") line_match = prog.match(line) # If there is a match and the id after the filtered attribute (attribut "id";) is in the list, we print it From cec6e6926b0980a54ed6f1536ead5844275b680d Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Sat, 13 Feb 2021 14:54:26 +0100 Subject: [PATCH 5/9] revert to a less intrusive change --- .../gtf_filter_by_attribute_values_list.py | 66 ++++++++++++++----- .../gtf_filter_by_attribute_values_list.xml | 6 +- 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.py b/tools/filters/gff/gtf_filter_by_attribute_values_list.py index 52db30dcd4c..cb641f40e05 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.py +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.py @@ -6,31 +6,63 @@ # from __future__ import print_function -import re import sys +def parse_gff_attributes(attr_str): + """ + Parses a GFF/GTF attribute string and returns a dictionary of name-value + pairs. The general format for a GFF3 attributes string is + name1=value1;name2=value2 + The general format for a GTF attribute string is + name1 "value1" ; name2 "value2" + The general format for a GFF attribute string is a single string that + denotes the interval's group; in this case, method returns a dictionary + with a single key-value pair, and key name is 'group' + """ + attributes_list = attr_str.split(";") + attributes = {} + for name_value_pair in attributes_list: + # Try splitting by space and, if necessary, by '=' sign. + pair = name_value_pair.strip().split(" ") + if len(pair) == 1: + pair = name_value_pair.strip().split("=") + if len(pair) == 1: + # Could not split for some reason -- raise exception? + continue + if pair == '': + continue + name = pair[0].strip() + if name == '': + continue + # Need to strip double quote from values + value = pair[1].strip(" \"") + attributes[name] = value + + if len(attributes) == 0: + # Could not split attributes string, so entire string must be + # 'group' attribute. This is the case for strictly GFF files. + attributes['group'] = attr_str + return attributes + + def gff_filter(gff_file, attribute_name, ids_file, output_file): # Put ids in dict for quick lookup. ids_dict = {} - with open(ids_file) as ids: - for line in ids: - ids_dict[line.split('\t')[0].strip()] = True + for line in open(ids_file): + ids_dict[line.split('\t')[0].strip()] = True # Filter GFF file using ids. - with open(output_file, 'w') as output: - with open(gff_file) as gff: - for line in gff: - # Keep commented lines - if line.startswith('#'): - output.write(line) - else: - # Create pattern using attribute to filter - prog = re.compile(r".*" + re.escape(attribute_name) + r" \"(.+?)\"\;") - line_match = prog.match(line) - # If there is a match and the id after the filtered attribute (attribut "id";) is in the list, we print it - if line_match and line_match.group(1) in ids_dict: - output.write(line) + with open(output_file, 'w') as output, open(gff_file) as ingff: + for line in ingff: + if not line or line.startswith('#'): + output.write(line) + continue + fields = line.split('\t') + attributes = parse_gff_attributes(fields[8]) + if (attribute_name in attributes) and (attributes[attribute_name] in ids_dict): + output.write(line) + output.close() if __name__ == "__main__": diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml index 0f5d0dadabc..99251dffb1d 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml @@ -1,7 +1,7 @@ - gtf_filter_by_attribute_values_list.py $input $attribute_name $ids $output + gtf_filter_by_attribute_values_list.py '$input' '$attribute_name' '$ids' '$output' @@ -19,10 +19,10 @@ - + - + From faed88a1bdafaaada5549d2b4e26ba8b3a26d6bb Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Sat, 13 Feb 2021 16:34:09 +0100 Subject: [PATCH 6/9] remove interpreter --- tools/filters/gff/gtf_filter_by_attribute_values_list.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml index 99251dffb1d..9dee10a0dca 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml @@ -1,7 +1,7 @@ - - gtf_filter_by_attribute_values_list.py '$input' '$attribute_name' '$ids' '$output' + + python '$__tool_directory__/gtf_filter_by_attribute_values_list.py' '$input' '$attribute_name' '$ids' '$output' From e4d9e1a29e898af13fcf66e7a4b809d10016bd8e Mon Sep 17 00:00:00 2001 From: M Bernt Date: Sat, 13 Feb 2021 20:38:05 +0100 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Nicola Soranzo --- tools/filters/gff/gtf_filter_by_attribute_values_list.py | 3 +-- tools/filters/gff/gtf_filter_by_attribute_values_list.xml | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.py b/tools/filters/gff/gtf_filter_by_attribute_values_list.py index cb641f40e05..8be10348557 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.py +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.py @@ -60,9 +60,8 @@ def gff_filter(gff_file, attribute_name, ids_file, output_file): continue fields = line.split('\t') attributes = parse_gff_attributes(fields[8]) - if (attribute_name in attributes) and (attributes[attribute_name] in ids_dict): + if attribute_name in attributes and attributes[attribute_name] in ids_dict: output.write(line) - output.close() if __name__ == "__main__": diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml index 9dee10a0dca..c75b12d65a2 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml @@ -1,8 +1,8 @@ - - python '$__tool_directory__/gtf_filter_by_attribute_values_list.py' '$input' '$attribute_name' '$ids' '$output' - + From 4be9bc6430ea4fcf849ffee2cce0740c70a26029 Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Sat, 13 Feb 2021 22:02:57 +0100 Subject: [PATCH 8/9] bump tool version --- tools/filters/gff/gtf_filter_by_attribute_values_list.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml index 9dee10a0dca..ef7461f3d4f 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml @@ -1,4 +1,4 @@ - + python '$__tool_directory__/gtf_filter_by_attribute_values_list.py' '$input' '$attribute_name' '$ids' '$output' From 610e31147a4e29ee1a4c5f5cdb3ba8005eeb2471 Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Sat, 13 Feb 2021 23:04:17 +0100 Subject: [PATCH 9/9] add python requirement --- tools/filters/gff/gtf_filter_by_attribute_values_list.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml index bf014ae8b7c..b1063b78827 100644 --- a/tools/filters/gff/gtf_filter_by_attribute_values_list.xml +++ b/tools/filters/gff/gtf_filter_by_attribute_values_list.xml @@ -1,5 +1,8 @@ + + python +