diff --git a/CITATION.cff b/CITATION.cff index 655e674..a9ee975 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,7 +2,7 @@ cff-version: 1.2.0 message: "If you use ModDotPlot in your research, please cite the article below." type: software title: "ModDotPlot" -version: "1.0.0" +version: "1.0.1" authors: - family-names: "Sweeten" given-names: "Alexander P." diff --git a/README.md b/README.md index ec18c3d..f234843 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Finally, confirm that the installation completed correctly and that your version | | | | (_) | (_| | | |__| | (_) | |_ | | | | (_) | |_ |_| |_|\___/ \__,_| |_____/ \___/ \__| |_| |_|\___/ \__| - v1.0.0 + v1.0.1 usage: moddotplot [-h] [--quiet] [{static,interactive}] ... diff --git a/pyproject.toml b/pyproject.toml index ecfc6c1..af0cc24 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "ModDotPlot" -version = "1.0.0" +version = "1.0.1" requires-python = ">=3.10,<3.15" dependencies = [ "pandas", diff --git a/src/moddotplot/annotations.py b/src/moddotplot/annotations.py index 5f3eb88..9986e09 100644 --- a/src/moddotplot/annotations.py +++ b/src/moddotplot/annotations.py @@ -17,11 +17,30 @@ ] +def _ucsc_directive_rows(filepath): + """Return zero-based rows containing UCSC ``track`` or ``browser`` lines.""" + + rows = [] + with open(filepath, "r", encoding="utf-8") as bed_file: + for line_number, line in enumerate(bed_file): + fields = line.lstrip().split(maxsplit=1) + if fields and fields[0] in {"track", "browser"}: + rows.append(line_number) + return rows + + def read_annotation_bed(filepath): """Read the BED3-BED9 subset used by ModDotPlot annotations.""" try: - dataframe = pd.read_csv(filepath, sep="\t", comment="#", header=None, dtype=str) + dataframe = pd.read_csv( + filepath, + sep="\t", + comment="#", + header=None, + dtype=str, + skiprows=_ucsc_directive_rows(filepath), + ) except pd.errors.EmptyDataError: return pd.DataFrame(columns=BED_COLUMNS[:3]) diff --git a/src/moddotplot/const.py b/src/moddotplot/const.py index facb179..d91f1c4 100755 --- a/src/moddotplot/const.py +++ b/src/moddotplot/const.py @@ -1,4 +1,4 @@ -VERSION = "1.0.0" +VERSION = "1.0.1" DIRECTION_COLORS = {"Forward": "#2166AC", "Reverse": "#D01C8B"} COLS = [ "#query_name", diff --git a/src/moddotplot/moddotplot.py b/src/moddotplot/moddotplot.py index 14adf04..dfebbd8 100755 --- a/src/moddotplot/moddotplot.py +++ b/src/moddotplot/moddotplot.py @@ -650,7 +650,7 @@ def _apply_static_config(args, config): else: args.fasta = config.get("fasta") args.load = config.get("load") - args.bed = config.get("bed") + args.bed = config.get("bed", args.bed) # Distance matrix commands args.kmer = config.get("kmer", args.kmer) diff --git a/tests/test_annotation_track.py b/tests/test_annotation_track.py index 2e6adad..e400551 100644 --- a/tests/test_annotation_track.py +++ b/tests/test_annotation_track.py @@ -51,6 +51,24 @@ def test_read_annotation_bed_accepts_bed3_through_bed9( assert dataframe.loc[0, "end"] == 20 +def test_read_annotation_bed_ignores_ucsc_directives(tmp_path): + bed_path = tmp_path / "annotations.bed" + bed_path.write_text( + 'track name="annotations" visibility=2 itemRgb="On"\n' + "browser position chr1:1-100\n" + "# comment\n" + "chr1\t10\t20\tfeature\t0\t+\t10\t20\t12,34,56\n" + ) + + dataframe = read_annotation_bed(bed_path) + + assert len(dataframe) == 1 + assert dataframe.loc[0, "chrom"] == "chr1" + assert dataframe.loc[0, "start"] == 10 + assert dataframe.loc[0, "end"] == 20 + assert dataframe.loc[0, "itemRgb"] == "12,34,56" + + @pytest.mark.parametrize( "record", [ diff --git a/tests/test_cli_runtime.py b/tests/test_cli_runtime.py index 8ea2e64..d0c2bc9 100644 --- a/tests/test_cli_runtime.py +++ b/tests/test_cli_runtime.py @@ -490,6 +490,42 @@ def test_static_config_accepts_sequence_selection(): assert args.sequence == ["chr1", "chr2"] +def test_static_config_preserves_cli_bed_when_config_omits_bed(): + args = cli.get_parser().parse_args( + [ + "static", + "--config", + "plot.json", + "--fasta", + "sequence.fa", + "--bed", + "provided.bed", + ] + ) + + cli._apply_static_config(args, {"identity": 90}) + + assert args.bed == "provided.bed" + + +def test_static_config_bed_overrides_cli_bed(): + args = cli.get_parser().parse_args( + [ + "static", + "--config", + "plot.json", + "--fasta", + "sequence.fa", + "--bed", + "provided.bed", + ] + ) + + cli._apply_static_config(args, {"bed": "configured.bed"}) + + assert args.bed == "configured.bed" + + def test_static_config_preserves_explicit_input_and_output_paths(): args = cli.parse_args( [