Source code for ensembl.tools.anno.simple_feature_annotation.eponine

# See the NOTICE file distributed with this work for additional information
# regarding copyright ownership.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Eponine is a probabilistic method for detecting transcription start sites (TSS)
in mammalian genomic sequence, with good specificity and excellent positional accuracy.
References
----------
:cite:`eponine`
"""
__all__ = ["run_eponine"]

import argparse
import logging
import logging.config
import multiprocessing
from os import PathLike
from pathlib import Path
import re
import subprocess
from typing import List

from ensembl.tools.anno.utils._utils import (
    check_exe,
    check_file,
    create_dir,
    check_gtf_content,
    get_sequence,
    get_seq_region_length,
    get_slice_id,
    slice_output_to_gtf,
)

logger = logging.getLogger("__name__")


[docs] def run_eponine( # pylint:disable=too-many-arguments, too-many-positional-arguments, too-many-locals genome_file: PathLike, output_dir: Path, num_threads: int = 1, java_bin: Path = Path("java"), eponine_bin: Path = Path( "/hps/software/users/ensembl/ensw/C8-MAR21-sandybridge/linuxbrew/opt/eponine/libexec/eponine-scan.jar" # pylint:disable=line-too-long ), eponine_threshold: float = 0.999, bedtools_bin: str = "/hps/software/users/ensembl/ensw/C8-MAR21-sandybridge/linuxbrew/bin/bedtools", # pylint:disable=line-too-long ) -> None: """ Run Eponine on genomic slices :param genome_file: Genome file path. :param genome_file: PathLike :param output_dir: Working directory path. :param output_dir: Path :param java_bin: Java path. :param java_bin: Path, default java :param eponine_bin: Eponine software path :param eponine_bin: Path :param num_threads: Number of threads. :param num_threads: int, default 1 :param eponine_threshold: Eponine threashold. :param eponine_threshold: float, default 0.999 :param bedtools_bin: Bedtools executable path. :param bedtools_bin: str, default "/hps/software/users/ensembl/ensw/C8-MAR21-sandybridge/linuxbrew/bin/bedtools"# pylint:disable=line-too-long :return: None :rtype: None """ check_file(Path(eponine_bin)) check_exe(java_bin) eponine_dir = create_dir(output_dir, "eponine_output") # os.chdir(str(eponine_dir)) output_file = eponine_dir / "annotation.gtf" if output_file.exists(): transcript_count = check_gtf_content(output_file, "simple_feature") if transcript_count > 0: logger.info("Eponine gtf file exists, skipping analysis") return logger.info("Creating list of genomic slices") seq_region_to_length = get_seq_region_length(genome_file, 5000) slice_ids_per_region = get_slice_id( seq_region_to_length, slice_size=1000000, overlap=0, min_length=5000 ) # pylint:disable=line-too-long eponine_cmd = [ str(java_bin), "-jar", str(eponine_bin), "-threshold", str(eponine_threshold), "-seq", ] logger.info("Running Eponine") pool = multiprocessing.Pool(int(num_threads)) # pylint:disable=consider-using-with for slice_id in slice_ids_per_region: pool.apply_async( _multiprocess_eponine, args=(eponine_cmd, slice_id, eponine_dir, Path(genome_file), bedtools_bin), ) pool.close() pool.join() slice_output_to_gtf(eponine_dir, "feature_id", "eponine", True, ".epo.gtf") for gtf_file in eponine_dir.glob("*.epo.gtf"): gtf_file.unlink()
def _multiprocess_eponine( eponine_cmd: List[str], slice_id: List[str], eponine_dir: Path, genome_file: Path, bedtools_bin: str, ) -> None: """ Run Eponine on multiprocess on genomic slices Args: eponine_cmd: Eponine command to execute. slice_id: List of slice IDs. eponine_dir : Eponine output directory path. genome_file : Genome file. bedtools_bin: Bedtools executable path. """ region_name, start, end = slice_id logger.info( "Processing slice to find transcription start sites with Eponine: %s:%s:%s", region_name, start, end, ) seq = get_sequence(region_name, int(start), int(end), 1, genome_file, eponine_dir, bedtools_bin) slice_name = f"{region_name}.rs{start}.re{end}" # with tempfile.TemporaryDirectory(dir=eponine_dir) as tmpdirname: slice_file = eponine_dir / f"{slice_name}.fa" with open(slice_file, "w+", encoding="utf8") as region_out: region_out.write(f">{region_name}\n{seq}\n") region_results = eponine_dir / f"{slice_name}.epo.gtf" output_file = Path(f"{slice_file}.epo") eponine_cmd = eponine_cmd.copy() eponine_cmd.append(str(slice_file)) logging.info(eponine_cmd) with open(output_file, "w+", encoding="utf8") as eponine_out: subprocess.run(eponine_cmd, stdout=eponine_out, check=True) _create_eponine_gtf(output_file, region_results, region_name) slice_file.unlink() output_file.unlink() def _create_eponine_gtf( output_file: Path, region_results: Path, region_name: str, ) -> None: """ Read the fasta file and save the content in gtf format All the genomic slices are collected in a single gtf output Args: output_file: GTF file with final results. region_results: GTF file with the results per region. region_name: Coordinates of genomic slice. """ with ( open(output_file, "r", encoding="utf8") as eponine_in, open(region_results, "w+", encoding="utf8") as eponine_out, ): feature_count = 1 for line in eponine_in: result_match = re.search(r"^" + region_name, line) if result_match: results = line.split() start = int(results[3]) end = int(results[4]) score = float(results[5]) strand = results[6] logging.info(results) # There's a one base offset on the reverse strand if strand == "-": start -= 1 end -= 1 gtf_line = ( f"{region_name}\tEponine\tsimple_feature\t" f"{start}\t{end}\t.\t{strand}\t.\t" f'feature_id "{feature_count}"; score "{score}";\n' ) eponine_out.write(gtf_line) feature_count += 1 def parse_args(): """Parse command line arguments.""" parser = argparse.ArgumentParser(description="Eponine's arguments") parser.add_argument("--genome_file", required=True, help="Genome file path") parser.add_argument("--output_dir", required=True, help="Output directory path") parser.add_argument("--num_threads", type=int, default=1, help="Number of threads") parser.add_argument("--java_bin", default="java", help="Java executable path") parser.add_argument( "--eponine_bin", default="/hps/software/users/ensembl/ensw/C8-MAR21-sandybridge/linuxbrew/opt/eponine/libexec/eponine-scan.jar", # pylint:disable=line-too-long help="Eponine executable path", ) parser.add_argument( "--eponine_threashold", type=float, default=0.999, help="Eponine threashold" ) # pylint:disable=line-too-long parser.add_argument( "--bedtools_bin", default="/hps/software/users/ensembl/ensw/C8-MAR21-sandybridge/linuxbrew/bin/bedtools", help="Bedtools executable path", ) return parser.parse_args() def main(): """Eponine's entry-point.""" args = parse_args() log_file_path = create_dir(args.output_dir, "log") / "eponine.log" loginipath = Path(__file__).parents[6] / "conf" / "logging.conf" logging.config.fileConfig( loginipath, defaults={"logfilename": str(log_file_path)}, disable_existing_loggers=False, ) run_eponine( args.genome_file, args.output_dir, args.num_threads, Path(args.java_bin), Path(args.eponine_bin), args.eponine_threashold, args.bedtools_bin, ) if __name__ == "__main__": main()