# Path: onecodex/lib/enums.py
# class Rank(BaseEnum):
#     """Python does not allow extending enums so we have to repeat the ranks."""
# 
#     Superkingdom = "superkingdom"
#     Kingdom = "kingdom"
#     Phylum = "phylum"
#     Class = "class"
#     Order = "order"
#     Family = "family"
#     Genus = "genus"
#     Species = "species"
#     Auto = "auto"
# 
# class Linkage(BaseEnum):
#     Average = "average"
#     Single = "single"
#     Complete = "complete"
#     Weighted = "weighted"
#     Centroid = "centroid"
#     Median = "median"
# 
# Path: onecodex/exceptions.py
# class OneCodexException(Exception):
#     pass
# 
# class PlottingException(OneCodexException):
#     """User-facing plotting error for cases when the user may take action to resolve the issue."""
# 
#     pass
# 
# Path: onecodex/viz/_primitives.py
# def prepare_props(title=None, height=None, width=None):
#     """Prepare key plotting kwargs for passing to Altair, which d/n like None values."""
#     props = {}
#     if title:
#         props["title"] = title  # None gets rendered as `None`
#     if height:
#         props["height"] = height  # None violates Vega JSON Schema
#     if width:
#         props["width"] = width  # None violates Vega JSON Schema
#     return props
# 
# def sort_helper(sort, values):
#     """Return a sorted list of values for the Altair chart axes."""
#     sort_order = None
# 
#     if callable(sort):
#         values = list(set(values))
#         sort_order = sort(values)
#     elif isinstance(sort, list):
#         if set(sort) != set(values):
#             raise OneCodexException("sort_x must have the same items as your dataset.")
#         sort_order = sort
#     elif sort:
#         raise OneCodexException(
#             "Please pass either a sorted list of values matching the axis labels \
#             or a function that returns a sorted list of labels"
#         )
# 
#     return sort_order
# 
# def get_base_classification_url():
#     return os.environ.get("ONE_CODEX_API_BASE", "https://app.onecodex.com") + "/classification/"


from onecodex.lib.enums import Rank, Linkage
from onecodex.exceptions import OneCodexException, PlottingException
from onecodex.viz._primitives import prepare_props, sort_helper, get_base_classification_url
        import altair as alt
        import pandas as pd
            df_taxa_cluster = df_sample_cluster
        else:
            df_sample_cluster = self.to_df(
                rank=rank, normalize=False, top_n=top_n, threshold=threshold
            )

            df_taxa_cluster = self.to_df(
                rank=rank, normalize=normalize, top_n=top_n, threshold=threshold
            )

        # applying clustering to determine order of taxa, or use custom sorting function if given
        if sort_y is None:
            taxa_cluster = df_taxa_cluster.ocx._cluster_by_taxa(linkage=linkage)
            taxa_cluster = taxa_cluster["labels_in_order"]
        else:
            taxa_cluster = sort_helper(sort_y, df["tax_name"])

        if sort_x is None:
            if haxis is None:
                # cluster samples only once
                sample_cluster = df_sample_cluster.ocx._cluster_by_sample(
                    rank=rank, metric=metric, linkage=linkage
                )
                labels_in_order = magic_metadata["Label"][sample_cluster["ids_in_order"]].tolist()
            else:
                if not (
                    pd.api.types.is_bool_dtype(df[magic_fields[haxis]])
                    or pd.api.types.is_categorical_dtype(df[magic_fields[haxis]])  # noqa
                    or pd.api.types.is_object_dtype(df[magic_fields[haxis]])  # noqa
                ):  # noqa

# Path: onecodex/lib/enums.py
# class Rank(BaseEnum):
#     """Python does not allow extending enums so we have to repeat the ranks."""
# 
#     Superkingdom = "superkingdom"
#     Kingdom = "kingdom"
#     Phylum = "phylum"
#     Class = "class"
#     Order = "order"
#     Family = "family"
#     Genus = "genus"
#     Species = "species"
#     Auto = "auto"
# 
# class Linkage(BaseEnum):
#     Average = "average"
#     Single = "single"
#     Complete = "complete"
#     Weighted = "weighted"
#     Centroid = "centroid"
#     Median = "median"
# 
# Path: onecodex/exceptions.py
# class OneCodexException(Exception):
#     pass
# 
# class PlottingException(OneCodexException):
#     """User-facing plotting error for cases when the user may take action to resolve the issue."""
# 
#     pass
# 
# Path: onecodex/viz/_primitives.py
# def prepare_props(title=None, height=None, width=None):
#     """Prepare key plotting kwargs for passing to Altair, which d/n like None values."""
#     props = {}
#     if title:
#         props["title"] = title  # None gets rendered as `None`
#     if height:
#         props["height"] = height  # None violates Vega JSON Schema
#     if width:
#         props["width"] = width  # None violates Vega JSON Schema
#     return props
# 
# def sort_helper(sort, values):
#     """Return a sorted list of values for the Altair chart axes."""
#     sort_order = None
# 
#     if callable(sort):
#         values = list(set(values))
#         sort_order = sort(values)
#     elif isinstance(sort, list):
#         if set(sort) != set(values):
#             raise OneCodexException("sort_x must have the same items as your dataset.")
#         sort_order = sort
#     elif sort:
#         raise OneCodexException(
#             "Please pass either a sorted list of values matching the axis labels \
#             or a function that returns a sorted list of labels"
#         )
# 
#     return sort_order
# 
# def get_base_classification_url():
#     return os.environ.get("ONE_CODEX_API_BASE", "https://app.onecodex.com") + "/classification/"


# Path: onecodex/viz/_heatmap.py
from onecodex.lib.enums import Rank, Linkage
from onecodex.exceptions import OneCodexException, PlottingException
from onecodex.viz._primitives import prepare_props, sort_helper, get_base_classification_url
        import altair as alt
        import pandas as pd

            df_taxa_cluster = df_sample_cluster
        else:
            df_sample_cluster = self.to_df(
                rank=rank, normalize=False, top_n=top_n, threshold=threshold
            )

            df_taxa_cluster = self.to_df(
                rank=rank, normalize=normalize, top_n=top_n, threshold=threshold
            )

        # applying clustering to determine order of taxa, or use custom sorting function if given
        if sort_y is None:
            taxa_cluster = df_taxa_cluster.ocx._cluster_by_taxa(linkage=linkage)
            taxa_cluster = taxa_cluster["labels_in_order"]
        else:
            taxa_cluster = sort_helper(sort_y, df["tax_name"])

        if sort_x is None:
            if haxis is None:
                # cluster samples only once
                sample_cluster = df_sample_cluster.ocx._cluster_by_sample(
                    rank=rank, metric=metric, linkage=linkage
                )
                labels_in_order = magic_metadata["Label"][sample_cluster["ids_in_order"]].tolist()
            else:
                if not (
                    pd.api.types.is_bool_dtype(df[magic_fields[haxis]])
                    or pd.api.types.is_categorical_dtype(df[magic_fields[haxis]])  # noqa
                    or pd.api.types.is_object_dtype(df[magic_fields[haxis]])  # noqa
                ):  # noqa

                    raise OneCodexException(