# Path: augur/utils.py
# def read_metadata(fname, query=None, as_data_frame=False):
#     return MetadataFile(fname, query, as_data_frame).read()
# 
# def read_tree(fname, min_terminals=3):
#     """Safely load a tree from a given filename or raise an error if the file does
#     not contain a valid tree.
# 
#     Parameters
#     ----------
#     fname : str
#         name of a file containing a phylogenetic tree
# 
#     min_terminals : int
#         minimum number of terminals required for the parsed tree as a sanity
#         check on the tree
# 
#     Raises
#     ------
#     InvalidTreeError
#         If the given file exists but does not seem to contain a valid tree format.
# 
#     Returns
#     -------
#     Bio.Phylo :
#         BioPython tree instance
# 
#     """
#     T = None
#     supported_tree_formats = ["newick", "nexus"]
#     for fmt in supported_tree_formats:
#         try:
#             T = Bio.Phylo.read(fname, fmt)
# 
#             # Check the sanity of the parsed tree to handle cases when non-tree
#             # data are still successfully parsed by BioPython. Too few terminals
#             # in a tree indicates that the input is not valid.
#             if T.count_terminals() < min_terminals:
#                 T = None
#             else:
#                 break
#         except ValueError:
#             # We cannot open the tree in the current format, so we will try
#             # another.
#             pass
# 
#     # If the tree cannot be loaded, raise an error to that effect.
#     if T is None:
#         raise InvalidTreeError(
#             "Could not read the given tree %s using the following supported formats: %s" % (fname, ", ".join(supported_tree_formats))
#         )
# 
#     return T
# 
# def get_numerical_dates(meta_dict, name_col = None, date_col='date', fmt=None, min_max_year=None):
#     if fmt:
#         numerical_dates = {}
# 
#         if isinstance(meta_dict, dict):
#             for k,m in meta_dict.items():
#                 v = m[date_col]
#                 numerical_dates[k] = get_numerical_date_from_value(
#                     v,
#                     fmt,
#                     min_max_year
#                 )
#         elif isinstance(meta_dict, pd.DataFrame):
#             strains = meta_dict.index.values
#             dates = meta_dict[date_col].apply(
#                 lambda date: get_numerical_date_from_value(
#                     date,
#                     fmt,
#                     min_max_year
#                 )
#             ).values
#             numerical_dates = dict(zip(strains, dates))
#     else:
#         if isinstance(meta_dict, dict):
#             numerical_dates = {k:float(v) for k,v in meta_dict.items()}
#         elif isinstance(meta_dict, pd.DataFrame):
#             strains = meta_dict.index.values
#             dates = meta_dict[date_col].astype(float)
#             numerical_dates = dict(zip(strains, dates))
# 
#     return numerical_dates
# 
# def write_json(data, file_name, indent=(None if os.environ.get("AUGUR_MINIFY_JSON") else 2), include_version=True):
#     """
#     Write ``data`` as JSON to the given ``file_name``, creating parent directories
#     if necessary. The augur version is included as a top-level key "augur_version".
# 
#     Parameters
#     ----------
#     data : dict
#         data to write out to JSON
#     file_name : str
#         file name to write to
#     indent : int or None, optional
#         JSON indentation level. Default is `None` if the environment variable `AUGUR_MINIFY_JSON`
#         is truthy, else 1
#     include_version : bool, optional
#         Include the augur version. Default: `True`.
# 
#     Raises
#     ------
#     OSError
#     """
#     #in case parent folder does not exist yet
#     parent_directory = os.path.dirname(file_name)
#     if parent_directory and not os.path.exists(parent_directory):
#         try:
#             os.makedirs(parent_directory)
#         except OSError: #Guard against race condition
#             if not os.path.isdir(parent_directory):
#                 raise
# 
#     if include_version:
#         data["generated_by"] = {"program": "augur", "version": get_augur_version()}
# 
#     with open(file_name, 'w', encoding='utf-8') as handle:
#         json.dump(data, handle, indent=indent, sort_keys=True)
# 
# class InvalidTreeError(Exception):
#     """Represents an error loading a phylogenetic tree from a filename.
#     """
#     pass


import numpy as np
import os, shutil, time, sys
    import json
from Bio import Phylo
from .utils import read_metadata, read_tree, get_numerical_dates, write_json, InvalidTreeError
from treetime.vcf_utils import read_vcf, write_vcf
from treetime.seq_utils import profile_maps
    from treetime import TreeTime
        from Bio import SeqRecord, Seq, Align
    from treetime import version as treetime_version
        from treetime import TreeAnc
            print("ERROR: a reference Fasta is required with VCF-format alignments", file=sys.stderr)
            return 1

        compress_seq = read_vcf(args.alignment, args.vcf_reference)
        aln = compress_seq['sequences']
        ref = compress_seq['reference']
        is_vcf = True
    else:
        aln = args.alignment

    print(f"augur refine is using TreeTime version {treetime_version}")

    # if not specified, construct default output file name with suffix _tt.nwk
    if args.output_tree:
        tree_fname = args.output_tree
    elif args.alignment:
        tree_fname = '.'.join(args.alignment.split('.')[:-1]) + '_tt.nwk'
    else:
        tree_fname = '.'.join(args.tree.split('.')[:-1]) + '_tt.nwk'

    if args.root and len(args.root) == 1: #if anything but a list of seqs, don't send as a list
        args.root = args.root[0]
    if args.keep_root:  # This flag overrides anything specified by 'root'
        args.root = None

    if args.timetree:
        # load meta data and covert dates to numeric
        if args.metadata is None:
            print("ERROR: meta data with dates is required for time tree reconstruction", file=sys.stderr)
            return 1

# Path: augur/utils.py
# def read_metadata(fname, query=None, as_data_frame=False):
#     return MetadataFile(fname, query, as_data_frame).read()
# 
# def read_tree(fname, min_terminals=3):
#     """Safely load a tree from a given filename or raise an error if the file does
#     not contain a valid tree.
# 
#     Parameters
#     ----------
#     fname : str
#         name of a file containing a phylogenetic tree
# 
#     min_terminals : int
#         minimum number of terminals required for the parsed tree as a sanity
#         check on the tree
# 
#     Raises
#     ------
#     InvalidTreeError
#         If the given file exists but does not seem to contain a valid tree format.
# 
#     Returns
#     -------
#     Bio.Phylo :
#         BioPython tree instance
# 
#     """
#     T = None
#     supported_tree_formats = ["newick", "nexus"]
#     for fmt in supported_tree_formats:
#         try:
#             T = Bio.Phylo.read(fname, fmt)
# 
#             # Check the sanity of the parsed tree to handle cases when non-tree
#             # data are still successfully parsed by BioPython. Too few terminals
#             # in a tree indicates that the input is not valid.
#             if T.count_terminals() < min_terminals:
#                 T = None
#             else:
#                 break
#         except ValueError:
#             # We cannot open the tree in the current format, so we will try
#             # another.
#             pass
# 
#     # If the tree cannot be loaded, raise an error to that effect.
#     if T is None:
#         raise InvalidTreeError(
#             "Could not read the given tree %s using the following supported formats: %s" % (fname, ", ".join(supported_tree_formats))
#         )
# 
#     return T
# 
# def get_numerical_dates(meta_dict, name_col = None, date_col='date', fmt=None, min_max_year=None):
#     if fmt:
#         numerical_dates = {}
# 
#         if isinstance(meta_dict, dict):
#             for k,m in meta_dict.items():
#                 v = m[date_col]
#                 numerical_dates[k] = get_numerical_date_from_value(
#                     v,
#                     fmt,
#                     min_max_year
#                 )
#         elif isinstance(meta_dict, pd.DataFrame):
#             strains = meta_dict.index.values
#             dates = meta_dict[date_col].apply(
#                 lambda date: get_numerical_date_from_value(
#                     date,
#                     fmt,
#                     min_max_year
#                 )
#             ).values
#             numerical_dates = dict(zip(strains, dates))
#     else:
#         if isinstance(meta_dict, dict):
#             numerical_dates = {k:float(v) for k,v in meta_dict.items()}
#         elif isinstance(meta_dict, pd.DataFrame):
#             strains = meta_dict.index.values
#             dates = meta_dict[date_col].astype(float)
#             numerical_dates = dict(zip(strains, dates))
# 
#     return numerical_dates
# 
# def write_json(data, file_name, indent=(None if os.environ.get("AUGUR_MINIFY_JSON") else 2), include_version=True):
#     """
#     Write ``data`` as JSON to the given ``file_name``, creating parent directories
#     if necessary. The augur version is included as a top-level key "augur_version".
# 
#     Parameters
#     ----------
#     data : dict
#         data to write out to JSON
#     file_name : str
#         file name to write to
#     indent : int or None, optional
#         JSON indentation level. Default is `None` if the environment variable `AUGUR_MINIFY_JSON`
#         is truthy, else 1
#     include_version : bool, optional
#         Include the augur version. Default: `True`.
# 
#     Raises
#     ------
#     OSError
#     """
#     #in case parent folder does not exist yet
#     parent_directory = os.path.dirname(file_name)
#     if parent_directory and not os.path.exists(parent_directory):
#         try:
#             os.makedirs(parent_directory)
#         except OSError: #Guard against race condition
#             if not os.path.isdir(parent_directory):
#                 raise
# 
#     if include_version:
#         data["generated_by"] = {"program": "augur", "version": get_augur_version()}
# 
#     with open(file_name, 'w', encoding='utf-8') as handle:
#         json.dump(data, handle, indent=indent, sort_keys=True)
# 
# class InvalidTreeError(Exception):
#     """Represents an error loading a phylogenetic tree from a filename.
#     """
#     pass


# Path: augur/refine.py
import numpy as np
import os, shutil, time, sys
    import json
from Bio import Phylo
from .utils import read_metadata, read_tree, get_numerical_dates, write_json, InvalidTreeError
from treetime.vcf_utils import read_vcf, write_vcf
from treetime.seq_utils import profile_maps
    from treetime import TreeTime
        from Bio import SeqRecord, Seq, Align
    from treetime import version as treetime_version
        from treetime import TreeAnc

            print("ERROR: a reference Fasta is required with VCF-format alignments", file=sys.stderr)
            return 1

        compress_seq = read_vcf(args.alignment, args.vcf_reference)
        aln = compress_seq['sequences']
        ref = compress_seq['reference']
        is_vcf = True
    else:
        aln = args.alignment

    print(f"augur refine is using TreeTime version {treetime_version}")

    # if not specified, construct default output file name with suffix _tt.nwk
    if args.output_tree:
        tree_fname = args.output_tree
    elif args.alignment:
        tree_fname = '.'.join(args.alignment.split('.')[:-1]) + '_tt.nwk'
    else:
        tree_fname = '.'.join(args.tree.split('.')[:-1]) + '_tt.nwk'

    if args.root and len(args.root) == 1: #if anything but a list of seqs, don't send as a list
        args.root = args.root[0]
    if args.keep_root:  # This flag overrides anything specified by 'root'
        args.root = None

    if args.timetree:
        # load meta data and covert dates to numeric
        if args.metadata is None:
            print("ERROR: meta data with dates is required for time tree reconstruction", file=sys.stderr)
            return 1

        metadata, columns = read_metadata(args.metadata)