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

# %% auto 0
__all__ = ['set_seed', 'most_common', 'context_chdir', 'generate_time_id', 'flatten_dict', 'unflatten_dict', 'NestedDict',
           'split_camel_case']

# %% ../nbs/utils.ipynb 3
import os
import random
import re
from contextlib import contextmanager
from pathlib import Path
from typing import Dict, List, Tuple, Union

import numpy as np
import pandas as pd
from fastcore.basics import patch

# %% ../nbs/utils.ipynb 4
def set_seed(seed):
    np.random.seed(seed%(2**32-1))
    random.seed(seed)

# %% ../nbs/utils.ipynb 6
from collections import Counter

def most_common(lst):
    """returns the most common element of a collection"""
    return Counter(lst).most_common(1)[0][0]

# %% ../nbs/utils.ipynb 8
@patch
def ls_sorted(self:Path):
    "ls but sorts files by name numerically"
    return self.ls().sorted(key=lambda f: int(f.with_suffix('').name))

# %% ../nbs/utils.ipynb 9
# ref: https://dev.to/teckert/changing-directory-with-a-python-context-manager-2bj8
@contextmanager
def context_chdir(path: Union[Path, str]):
    """Sets the cwd within the context"""
    origin = Path().absolute()
    try:
        os.chdir(path)
        yield
    finally:
        os.chdir(origin)

# %% ../nbs/utils.ipynb 11
from datetime import datetime

def generate_time_id(dt=None):
    """generates a string id from given datetime or now"""
    return (dt or datetime.now()).isoformat().rsplit('.', 1)[0].replace(':', '-')

# %% ../nbs/utils.ipynb 14
def flatten_dict(d: Dict, sep='.') -> Dict:
    records = pd.json_normalize(d, sep=sep).to_dict(orient='records')
    if len(records):
        return records[0]
    return {}

def unflatten_dict(d: Dict, sep='.') -> Dict:
    res = {}
    for k, v in d.items():
        subkeys = k.split(sep)
        container = res
        for subkey in subkeys[:-1]:
            if subkey not in container:
                container[subkey] = {}
            container = container[subkey]
        container[subkeys[-1]] = v
    return res

# %% ../nbs/utils.ipynb 17
class NestedDict(dict):
    def __init__(self, data, sep='.'):
        super().__init__(data)
        self.sep = sep
    
    def at(self, keys: Union[str, List, Tuple], default=None):
        if isinstance(keys, str):
            keys = keys.split(self.sep)
        node = self
        for key in keys:
            if key not in node:
                return default
            node = node.get(key)
        return node

    def set(self, keys: Union[str, List, Tuple], value):
        if isinstance(keys, str):
            keys = keys.split(self.sep)
        node = self
        last_key = keys.pop()
        for key in keys:
            if key not in node:
                node[key] = dict()
            node = node[key]
        node[last_key] = value

    def flat(self) -> Dict:
        return flatten_dict(self, sep=self.sep)
    
    @classmethod
    def from_flat_dict(cls, data, sep='.'):
        return cls(unflatten_dict(data, sep=sep))
     

# %% ../nbs/utils.ipynb 20
def split_camel_case(input_str):
    # Use regular expression to find word boundaries in camel case
    matches = re.finditer('.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', input_str)
    # Extract the words and return as a list
    return [m.group(0) for m in matches]
