# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/dictUtil.ipynb (unless otherwise specified).

__all__ = ['printDict', 'allKeysInDict', 'filterDt', 'stripDict', 'hashDict', 'saveDictToFile', 'loadDictFromFile',
           'saveStringToFile', 'loadStringFromFile', 'genSchema', 'printYaml', 'loadYaml', 'writeYaml', 'hash_dict',
           'deepUpdate']

# Cell
import yaml
from beartype import beartype

# Cell
def printDict(d:dict, length:int = 10, space = 0):
  '''
  print dictionary as first length value of values \n
  d: dict: the dictionary to be printed \n
  length: int: if the value is a string, then it will print the first length letter of the value, default = 10 \n
  space: int: the amount of additional space to be added for each nested dictionary, default = 0
  '''
  if type(d) != dict:
    print('this is not a dict')
    print(d)
  else:
    for k, v in d.items():
      if type(v) == dict:
        print(f"{' '*space}{k}")
        printDict(v, space=space+1)
      else:
        print(f"{' '*space}{k} : {v[:length] if type(v)==str else v}")

# Cell
def allKeysInDict(inputDict:dict, keys:list):
  '''
  checks whether all the keys given in the list are also keys in the inputDict dictionary \n
  inputDict: dict: the dictionary that will be used to check whether all the keys are in this dictionary \n
  keys: list: the list of keys
  '''
  return all(key in inputDict for key in keys)

# Cell
def filterDt(dtDict:dict):
  '''
  convert unjsonable datetime object to timestamp in the dictionary, this works for nested dictionary as well \n
  dtDict: dict: the dictionary inputted to convert the datetime object of its value
  '''
  from datetime import datetime
  return {k: (filterDt(v) if type(v) == dict else v) if type(v) != datetime else v.timestamp()
            for k,v in dtDict.items()}

# Cell
def stripDict(data:dict):
  '''
  if the value in the dictionary is a string, it will 'strip' the value to make it more clear \n
  data: dict: the dictionary inputted to be 'stripped'
  '''
  return {k: v.strip() if type(v) == str else v for k,v in data.items()}

# Cell
import hashlib, pickle, base64
def hashDict(data:dict, hasher= hashlib.sha1(), encoder = pickle.dumps):
  '''
  hashes the dictionary inputted \n
  data: dict: the dictionary inputted to be 'hashed'
  '''
  hasher.update(encoder(data))
  rawHash = hasher.digest()
  return base64.b64encode(rawHash).decode()

# Cell
import pickle
def saveDictToFile(data:dict, path:str):
  '''
  saves the dictionary to the file directed by the path \n
  data: dict: the dictionary to be saved \n
  path: str: the file path to the file the dictionary is going to be saved
  '''
  with open(path, 'wb')as f:
    pickle.dump(data,f,protocol=pickle.HIGHEST_PROTOCOL)

def loadDictFromFile(path:str):
  '''
  returns the dictionary that is saved in the file \n
  path: str: the path taken to the file of the dictionary
  '''
  with open(path, 'rb') as f:
    return pickle.load(f)

# Cell
def saveStringToFile(data:str, path:str):
  '''
  saves the string to the file directed by the path \n
  data: str: the string to be saved \n
  path: str: the file path to the file the string is going to be saved
  '''
  with open(path, 'w')as f:
    f.write(data)
def loadStringFromFile(path:str):
  '''
  returns the string that is saved in the file \n
  path: str: the path taken to the file of the string
  '''
  with open(path, 'r')as f:
    return f.read()

# Cell
def genSchema(inputDict:dict, format_='yaml')->(dict,str):
  '''
  generate a json schema from dict \n
  format: return schema in json or yaml, 'both' can be inputted to return a tuple of (json, yaml), output = dict or str, default = 'yaml' \n
  inputDict: dict: the dict inputted to be used to generate the schema
  '''
  from genson import SchemaBuilder
  import yaml
  builder = SchemaBuilder()
  builder.add_object(inputDict)
  schema = builder.to_schema()
  if format_=='yaml':
    return yaml.dump(schema)
  elif format_ == 'json':
    return schema
  elif format_ == 'both':
    return schema, yaml.dump(schema)
  else:
    return schema, yaml.dump(schema)

# Cell
@beartype
def printYaml(input_:dict, returnYaml = False)->(str, None):
  yamlStr:str =  yaml.dump(input_)
  print(yamlStr)
  if returnYaml:
    return yamlStr

# Cell
@beartype
def loadYaml(path:str, loader = yaml.FullLoader)->dict:
  '''load yaml string
    input:
      path:str: path of the data to load
    return:
      diet of the yaml

  '''
  with open(path) as f:
    r = yaml.load(f.read(), Loader = loader)
  return r

# Cell
@beartype
def writeYaml(path:str, data:dict):
  with open(path, 'w') as f:
    f.write(yaml.dump(data))


# Cell
import functools
import json
import ujson
import pickle
def hash_dict(func):
    """Transform mutable dictionnary
    Into immutable
    Useful to be compatible with cache
    """
    class HDict(dict):
        def __hash__(self):
          return hash(ujson.dumps(self, sort_keys=True))
#           return hash(tuple(frozenset(sorted(self.items()))))
#           print(self)
#           return hash(hashDict(self))
#             return hash(frozenset(self.items()))

    @functools.wraps(func)
    def wrapped(*args, **kwargs):
        args = tuple([HDict(arg) if isinstance(arg, dict) else arg for arg in args])
        kwargs = {k: HDict(v) if isinstance(v, dict) else v for k, v in kwargs.items()}
        return func(*args, **kwargs)
    return wrapped

# Cell
import collections
def deepUpdate(d, u):
    for k, v in u.items():
        if isinstance(d, collections.Mapping):
            if isinstance(v, collections.Mapping):
                r = deepUpdate(d.get(k, {}), v)
                d[k] = r
            else:
                d[k] = u[k]
        else:
            d = {k: u[k]}
    return d