# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_utils.ipynb.

# %% auto 0
__all__ = ['namestr', 'list_files', 'make_paths', 'plot_hist', 'plot_reduction', 'test_eqs', 'validate', 'cudamem',
           'get_description', 'is_colab']

# %% ../nbs/00_utils.ipynb 2
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from fastcore.all import *
from .imports import *
from .torch_imports import *
from .fastai_imports import *

# %% ../nbs/00_utils.ipynb 5
def namestr(obj, namespace=None):
    "Returns the name of the object `obj` passed"
    return [name for name in namespace if namespace[name] is obj]

# %% ../nbs/00_utils.ipynb 8
def list_files(startpath):
    """ [simulates the linux tree cmd] 
    (https://stackoverflow.com/questions/9727673/list-directory-tree-structure-in-python)
    """ 
    
    if isinstance(startpath, Path): startpath = str(startpath) 
    for root, dirs, files in os.walk(startpath):
        level = root.replace(startpath, '').count(os.sep)
        indent = ' ' * 4 * (level)
        print('{}{}/'.format(indent, os.path.basename(root)))
        subindent = ' ' * 4 * (level + 1)
        for f in files:
            print('{}{}'.format(subindent, f))

# %% ../nbs/00_utils.ipynb 9
def make_paths(path, prefix=None):
    """
    with `path` as basedir, makes data and models dir and 
    returns a dictionary of relevant pathlib objects
    """
    path_data = path/'data'
    path_model = path/'models'

    path_model.mkdir(exist_ok=True)
    path_data.mkdir(exist_ok=True)
    (path_model/'collab').mkdir(exist_ok=True)

    data = path_data/(prefix+'.csv')
    dls_lm_path, dls_lm_r_path = path_model/f"{prefix}_dls_lm.pkl", path_model/f"{prefix}_dls_lm_r.pkl"
    dls_lm_vocab_path, dls_lm_vocab_r_path = path_model/f"{prefix}_dls_lm_vocab.pkl", path_model/f"{prefix}_dls_lm_vocab_r.pkl"
    lm_path, lm_r_path = path_model/f"{prefix}_lm.pth", path_model/f"{prefix}_lm_r.pth"
    lm_finetuned_path, lm_finetuned_r_path = path_model/f"{prefix}_lm_finetuned.pth", path_model/f"{prefix}_lm_finetuned_r.pth"
    dsets_clas_path, dsets_clas_r_path = path_model/f"{prefix}_dset_clas.pkl", path_model/f"{prefix}_dset_clas_r.pkl"
    dls_clas_path, dls_clas_r_path = path_model/f"{prefix}_dls_clas.pkl", path_model/f"{prefix}_dls_clas_r.pkl"
    clas_path, clas_r_path = path_model/f"{prefix}_clas.pth", path_model/f"{prefix}_clas_r.pth"
    collab_bootst_path = path_model/f"{prefix}_tok_lbl_info.pkl"
    collab_data_path = path_data/f"{prefix}_tok_lbl.ft"
    collab_tok_path = path_data/f"{prefix}_tok.ft"
    collab_lbl_path = path_data/f"{prefix}_lbl.ft"
    dls_collab_path = path_model/f"{prefix}_dls_collab.pkl"
    dls_learn_rank_path = path_model/f"{prefix}_dls_learn_rank.pkl"
    collab_path = path_model/'collab'/f"{prefix}_collab.pth"
    plist = [path, path_data, path_model, 
             data, 
             dls_lm_path, dls_lm_r_path,
             dls_lm_vocab_path, dls_lm_vocab_r_path,
             lm_path, lm_r_path,
             lm_finetuned_path, lm_finetuned_r_path,
             dsets_clas_path, dsets_clas_r_path,
             dls_clas_path, dls_clas_r_path,
             clas_path, clas_r_path,
             collab_bootst_path,
             collab_data_path,
             collab_tok_path,
             collab_lbl_path,
             dls_collab_path,
             dls_learn_rank_path,
             collab_path]
    pdir = {}
    for o in plist:  pdir[namestr(o, locals())[0]] = o
    return pdir

# %% ../nbs/00_utils.ipynb 11
def plot_hist(data, x_label=None, y_label=None, title="Histogram"):
    n, bins, pathches = plt.hist(data)
    plt.grid(axis='y', color='b')
    # plt.yscale('log')
    if x_label is not None: plt.xlabel(x_label)
    if y_label is not None: plt.ylabel(y_label)
    maxfreq = n.max()
    plt.ylim(ymax=np.ceil(maxfreq / 10) * 10 if maxfreq % 10 else maxfreq + 10)
    plt.title(title);

# %% ../nbs/00_utils.ipynb 12
def plot_reduction(X, tSNE=True, n_comps=None, perplexity=30, figsize=(6,4)):
    """
    PCA on X and plots the first two principal components, returns the decomposition 
    and the explained variances for each directions,
    if `tSNE` then does a tSNE after PCA.
    """
    reduction = "tSNE" if tSNE else "PCA"
    pca = PCA(n_components=n_comps, svd_solver="full")
    X_red = pca.fit_transform(X)
    if tSNE:
        tsne = TSNE(n_components=2, perplexity=perplexity)
        X_red = tsne.fit_transform(X_red[:, :50])
    fig = plt.figure(figsize=figsize)
    ax = fig.add_subplot(1,1,1)
    plt.scatter(X_red[:, 0], X_red[:, 1], marker='x')
    ax.set_xlabel("1st component")
    ax.set_ylabel("2nd component")
    ax.set_title(f"{reduction} Decomposition")
    plt.show()
    return X_red, pca.explained_variance_ratio_

# %% ../nbs/00_utils.ipynb 13
def test_eqs(*args):
    for i in range(len(args)-1):
        test_eq(args[i], args[i+1])

# %% ../nbs/00_utils.ipynb 15
from fastai.callback.tracker import SaveModelCallback

# %% ../nbs/00_utils.ipynb 16
def validate(learner, cb=SaveModelCallback, **kwargs):
    "validates a `learner` within a context manager after temporarily removing `cb` if it exists"
    save_cb_idx = learner.cbs.argfirst(lambda o: isinstance(o, cb))
    if save_cb_idx is None:
        print(learner.validate(**kwargs))
        return
    print(f'best so far = {learner.cbs[save_cb_idx].best}')
    with learner.removed_cbs(learner.cbs[save_cb_idx]):
        vals = learner.validate(**kwargs)
        names = learner.recorder._valid_mets.map(Self.name())
        print('\n'.join([f"{n} = {v}" for n,v in zip(names,vals)]))
    save_cb_idx = learner.cbs.argfirst(lambda o: isinstance(o, cb))
    print(f'best so far = {learner.cbs[save_cb_idx].best}')
    return vals

# %% ../nbs/00_utils.ipynb 17
def cudamem(device=default_device()):
    using = torch.cuda.max_memory_reserved(device)/1024**3
    total = torch.cuda.get_device_properties(device).total_memory/1024**3
    print(f"GPU: {torch.cuda.get_device_name(default_device())}")
    print(f"You are using {using} GB")
    print(f"Total GPU memory = {total} GB")

# %% ../nbs/00_utils.ipynb 18
import requests
from bs4 import BeautifulSoup
def get_description(codes):
    "descriptions of ICD10 codes"
    # Initialize an empty dictionary to store code descriptions
    icd10_descriptions = {}
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
    }
    with requests.Session() as session:
        for code in codes:
            url_cm = f"https://icd10coded.com/cm/{code}"
            url_pcs = f"https://icd10coded.com/pcs/{code}"
            
            for url in (url_pcs, url_cm):   
                with session.get(url, headers=headers, timeout=5) as response:
                    if response.status_code == 200:
                        soup = BeautifulSoup(response.content, 'html.parser')
                        description_element = soup.find('span', {'class': 'lead'})
                        if description_element:
                            description = description_element.text.strip()
                            icd10_descriptions[code] = description
                            break
                        else:
                            icd10_descriptions[code] = "Description not found"
                    else:
                        # print(f"An error occurred.")
                        icd10_descriptions[code] = "Code not found"
    return icd10_descriptions

# %% ../nbs/00_utils.ipynb 19
def is_colab():
    # Check if the 'google.colab' module is available
    try:
        import google.colab
        IN_COLAB = True
    except ImportError:
        IN_COLAB = False

    # Check for specific environment variables typically set in Colab
    if 'COLAB_GPU' in os.environ:
        IN_COLAB = True

    if IN_COLAB:
        print("Running in Google Colab")
    else:
        print("Not running in Google Colab")

    if IN_COLAB:
        try:
            subprocess.check_call(["pip", "install", 'GitPython'])
            print(f"Successfully installed GitPython")
            from git import Repo
            repo = Repo.clone_from('https://github.com/debjyotiSRoy/xcube.git', Path.cwd().parent/'xcube')
            repo.git.checkout('plant')
        except subprocess.CalledProcessError as e:
            print(f"Failed to install GitPython: {e}")
    return IN_COLAB
