# Path: diagnostic_primers/blast.py
# def build_commands(collection, blastexe, blastdb, outdir=None, existingfiles=[]):
# def build_blastscreen_cmd(queryfile, blastexe, blastdb, outdir=None):
# def apply_screen(blastfile, primerjson, jsondir=None, maxaln=15):
# def parse_blasttab(fhandle):
# 
# Path: diagnostic_primers/scripts/tools.py
# def collect_existing_output(dirpath, step, args):
#     """Returns a list of existing output files at dirpath
# 
#     :param dirpath:       path to existing output directory
#     :param step:          the pipeline step being run
#     :param args:          command-line arguments for the run
#     """
#     # The suffixes dict has step:file_suffix pairs. Step refers to the pdp
#     # pipeline step, and the file_suffix the the suffix of files that are
#     # written to the output at the end of a called third-party application
#     # job. So, the prodigal step calls prodigal, which writes a .gff file.
#     #  When in --recovery mode, the presence of a .gff file is taken to
#     #  indicate that the third-party tool ran to completion.
#     suffixes = {
#         "eprimer3": ".eprimer3",
#         "primer3": ".primer3",
#         "prodigal": ".gff",
#         "alnvar": ".filter",
#         "blastscreen": ".blasttab",
#         "primersearch": ".primersearch",
#         "extract": ".aln",
#     }
#     try:
#         existingfiles = [
#             fname
#             for fname in os.listdir(dirpath)
#             if os.path.splitext(fname)[-1] == suffixes[step]
#         ]
#     except KeyError:
#         raise PDPScriptError(
#             "PDP step {} not recognised when collecting output".format(step)
#         )
#     return existingfiles
# 
# def create_output_directory(outdirname, force, logger):
#     """Create output directory, respecting cmd-line arguments
# 
#     - outdirname          path to output directory
#     - force               force creation True/False
#     - logger              logger for program
# 
#     Attempts to create a directory, but will not attempt to overwrite
#     unless the command-line argument to force is set True
#     """
#     if os.path.exists(outdirname) and not force:
#         logger.error(
#             "Output directory %s exists - not overwriting (exiting)", outdirname
#         )
#         raise SystemExit(1)
#     if force:
#         logger.warning(
#             "Output directory %s exists: forcing use and potential overwrite",
#             outdirname,
#         )
#     else:
#         logger.info("Creating output directory %s", outdirname)
#     os.makedirs(outdirname, exist_ok=True)
# 
# def load_config_json(args, logger):
#     """Load JSON format config to PDPCollection."""
#     pdpc = config.PDPCollection()
#     try:
#         logger.info("Loading config from %s", args.infilename)
#         pdpc.from_json(args.infilename)
#     except config.ConfigSyntaxError:
#         logger.error("Could not read config file %s (exiting)", args.infilename)
#         logger.error(last_exception())
#         raise SystemExit(1)
#     except FileNotFoundError:
#         logger.error("Config file %s does not exist (exiting)", args.infilename)
#         logger.error(last_exception)
#         raise SystemExit(1)
#     return pdpc
# 
# def log_clines(clines, logger):
#     """Log command-lines, one per line."""
#     logger.info(
#         "...%d commands returned:\n%s"
#         % (len(clines), "\n".join(["  %s" % c for c in clines]))
#     )
# 
# def run_parallel_jobs(clines, args, logger):
#     """Run the passed command-lines in parallel."""
#     logger.info("Running jobs using scheduler: %s" % args.scheduler)
#     # Pass lines to scheduler and run
#     if args.scheduler == "multiprocessing":
#         retvals = multiprocessing.run(clines, workers=args.workers)
#         if retvals != 0:
#             logger.error("At least one run has problems (exiting).")
#             raise SystemExit(1)
#         logger.info("Runs completed without error.")
#     elif args.scheduler == "SGE":
#         joblist = [
#             sge_jobs.Job("pdp_%06d" % idx, cmd) for idx, cmd in enumerate(clines)
#         ]
#         sge.run_dependency_graph(joblist, logger=logger)
#     else:
#         raise ValueError(
#             "Scheduler must be one of "
#             + "[multiprocessing|SGE], got %s" % args.scheduler
#         )


from tqdm import tqdm
from diagnostic_primers import blast
from diagnostic_primers.scripts.tools import (
    collect_existing_output,
    create_output_directory,
    load_config_json,
    log_clines,
    run_parallel_jobs,
)
        )
        existingfiles = collect_existing_output(args.bs_dir, "blastscreen", args)
        logger.info(
            "Existing files found:\n\t%s", "\n\t".join([_ for _ in existingfiles])
        )

    # Run BLASTN search with primer sequences
    logger.info("Building BLASTN screen command-lines...")
    clines = blast.build_commands(
        coll, args.bs_exe, args.bs_db, args.bs_dir, existingfiles
    )
    if clines:
        pretty_clines = [str(c).replace(" -", " \\\n          -") for c in clines]
        log_clines(pretty_clines, logger)
        run_parallel_jobs(clines, args, logger)
        logger.info("BLASTN+ search complete")
    else:
        logger.warning(
            "No BLASTN+ jobs were scheduled (you may see this if the --recovery option is active)"
        )

    # Amend primer JSON files to remove screened primers
    for blastout, indata in tqdm(
        zip([cline.out for cline in clines], coll.data),
        desc="removing screened primers",
        disable=args.disable_tqdm,
    ):
        logger.info(
            "Amending primer file %s with results from %s", indata.primers, blastout
        )

# Path: diagnostic_primers/blast.py
# def build_commands(collection, blastexe, blastdb, outdir=None, existingfiles=[]):
# def build_blastscreen_cmd(queryfile, blastexe, blastdb, outdir=None):
# def apply_screen(blastfile, primerjson, jsondir=None, maxaln=15):
# def parse_blasttab(fhandle):
# 
# Path: diagnostic_primers/scripts/tools.py
# def collect_existing_output(dirpath, step, args):
#     """Returns a list of existing output files at dirpath
# 
#     :param dirpath:       path to existing output directory
#     :param step:          the pipeline step being run
#     :param args:          command-line arguments for the run
#     """
#     # The suffixes dict has step:file_suffix pairs. Step refers to the pdp
#     # pipeline step, and the file_suffix the the suffix of files that are
#     # written to the output at the end of a called third-party application
#     # job. So, the prodigal step calls prodigal, which writes a .gff file.
#     #  When in --recovery mode, the presence of a .gff file is taken to
#     #  indicate that the third-party tool ran to completion.
#     suffixes = {
#         "eprimer3": ".eprimer3",
#         "primer3": ".primer3",
#         "prodigal": ".gff",
#         "alnvar": ".filter",
#         "blastscreen": ".blasttab",
#         "primersearch": ".primersearch",
#         "extract": ".aln",
#     }
#     try:
#         existingfiles = [
#             fname
#             for fname in os.listdir(dirpath)
#             if os.path.splitext(fname)[-1] == suffixes[step]
#         ]
#     except KeyError:
#         raise PDPScriptError(
#             "PDP step {} not recognised when collecting output".format(step)
#         )
#     return existingfiles
# 
# def create_output_directory(outdirname, force, logger):
#     """Create output directory, respecting cmd-line arguments
# 
#     - outdirname          path to output directory
#     - force               force creation True/False
#     - logger              logger for program
# 
#     Attempts to create a directory, but will not attempt to overwrite
#     unless the command-line argument to force is set True
#     """
#     if os.path.exists(outdirname) and not force:
#         logger.error(
#             "Output directory %s exists - not overwriting (exiting)", outdirname
#         )
#         raise SystemExit(1)
#     if force:
#         logger.warning(
#             "Output directory %s exists: forcing use and potential overwrite",
#             outdirname,
#         )
#     else:
#         logger.info("Creating output directory %s", outdirname)
#     os.makedirs(outdirname, exist_ok=True)
# 
# def load_config_json(args, logger):
#     """Load JSON format config to PDPCollection."""
#     pdpc = config.PDPCollection()
#     try:
#         logger.info("Loading config from %s", args.infilename)
#         pdpc.from_json(args.infilename)
#     except config.ConfigSyntaxError:
#         logger.error("Could not read config file %s (exiting)", args.infilename)
#         logger.error(last_exception())
#         raise SystemExit(1)
#     except FileNotFoundError:
#         logger.error("Config file %s does not exist (exiting)", args.infilename)
#         logger.error(last_exception)
#         raise SystemExit(1)
#     return pdpc
# 
# def log_clines(clines, logger):
#     """Log command-lines, one per line."""
#     logger.info(
#         "...%d commands returned:\n%s"
#         % (len(clines), "\n".join(["  %s" % c for c in clines]))
#     )
# 
# def run_parallel_jobs(clines, args, logger):
#     """Run the passed command-lines in parallel."""
#     logger.info("Running jobs using scheduler: %s" % args.scheduler)
#     # Pass lines to scheduler and run
#     if args.scheduler == "multiprocessing":
#         retvals = multiprocessing.run(clines, workers=args.workers)
#         if retvals != 0:
#             logger.error("At least one run has problems (exiting).")
#             raise SystemExit(1)
#         logger.info("Runs completed without error.")
#     elif args.scheduler == "SGE":
#         joblist = [
#             sge_jobs.Job("pdp_%06d" % idx, cmd) for idx, cmd in enumerate(clines)
#         ]
#         sge.run_dependency_graph(joblist, logger=logger)
#     else:
#         raise ValueError(
#             "Scheduler must be one of "
#             + "[multiprocessing|SGE], got %s" % args.scheduler
#         )


# Path: diagnostic_primers/scripts/subcommands/subcmd_blastscreen.py
from tqdm import tqdm
from diagnostic_primers import blast
from diagnostic_primers.scripts.tools import (
    collect_existing_output,
    create_output_directory,
    load_config_json,
    log_clines,
    run_parallel_jobs,
)

        )
        existingfiles = collect_existing_output(args.bs_dir, "blastscreen", args)
        logger.info(
            "Existing files found:\n\t%s", "\n\t".join([_ for _ in existingfiles])
        )

    # Run BLASTN search with primer sequences
    logger.info("Building BLASTN screen command-lines...")
    clines = blast.build_commands(
        coll, args.bs_exe, args.bs_db, args.bs_dir, existingfiles
    )
    if clines:
        pretty_clines = [str(c).replace(" -", " \\\n          -") for c in clines]
        log_clines(pretty_clines, logger)
        run_parallel_jobs(clines, args, logger)
        logger.info("BLASTN+ search complete")
    else:
        logger.warning(
            "No BLASTN+ jobs were scheduled (you may see this if the --recovery option is active)"
        )

    # Amend primer JSON files to remove screened primers
    for blastout, indata in tqdm(
        zip([cline.out for cline in clines], coll.data),
        desc="removing screened primers",
        disable=args.disable_tqdm,
    ):
        logger.info(
            "Amending primer file %s with results from %s", indata.primers, blastout
        )

        newprimers = blast.apply_screen(