"""
Encode dictionaries in strings and decode them. Useful for automatically asigning filenames.
"""
import numpy as np
import os
##############################################################################################################################
""" I. Getopt utils """
##############################################################################################################################
[docs]def encoder(x, ndigits=2, iterables=(list, tuple, np.ndarray), iterable_maxsize=3):
"""x -> string version of x"""
if isinstance(x, float):
if x == int(x):
return str(int(x))
else:
return str(round(x, ndigits=ndigits)).replace('.', '--')
elif isinstance(x, int):
return str(x)
elif callable(x):
return x.__name__
elif isinstance(x, dict):
if len(x) >= iterable_maxsize:
return "{}-values".format(str(len(x)))
else:
return dict_to_id(x, ndigits=ndigits, join_char="-")
elif isinstance(x, iterables):
if len(x) >= iterable_maxsize:
return "{}-values".format(str(len(x)))
else:
return '-'.join([encoder(sub_x) for sub_x in x])
else:
return str(x)
[docs]def decoder(x, iterables=(list, tuple, np.ndarray)):
"""string version of x -> x"""
if x.lower() == "none":
return None
elif x.lower() == "false":
return False
elif x.lower() == "true":
return True
elif "--" in x:
return float(x.replace("--", "."))
elif isinstance(x, iterables):
return [decoder(sub_x) for sub_x in x]
else:
try:
return int(x)
except:
return x
[docs]def getopt_printer(opts):
"""Prints getopt input in a readable way."""
print('\n'.join(f'{opt} => {arg}' for opt, arg in (("Args", "Values"), *opts)))
[docs]def dict_to_id(*args, ndigits=2, join_char="_", **kwargs):
"""Generate ID of the form k1-v1_k2-v2... for k_i, v_i keys and values of the dictionary d or the kwargs."""
key_formatter = lambda k: k.replace("_", "-")
d = args[0] if len(args) > 0 else kwargs
return join_char.join([f"{key_formatter(k)}-{encoder(d[k], ndigits=ndigits)}" for k in sorted(d.keys())])
[docs]def id_to_dict(identifier):
"""Inverse of dict_to_id."""
s = identifier.split("/")[-1] # retain filename only
s = os.path.splitext(s)[0] # remove extension
d = {}
for split in s.split("_"):
var_value = split.split("-")
if len(var_value) > 1:
if "" in var_value: # value is a float
var_value_arr = np.array(var_value)
idx_dot = np.argwhere(var_value_arr == "")[0, 0]
key_idx = 0 if idx_dot == 2 else slice(0, idx_dot-2)
d["-".join(var_value[key_idx])] = decoder(f"{var_value_arr[idx_dot-1]}--{var_value_arr[idx_dot+1]}")
else:
d["-".join(var_value[:-1])] = decoder(var_value[-1])
return d
[docs]def id_updater(filename, update_dict, mode="add"):
"""
Modifies filename by updating the underlying dict.
Attrs:
- filename: id to be modified
- update_dict: dict to use for updating the id. if update_dict={} => filename rearranged according to other_utils.dict_to_id.
- mode: - "add": add update_dict to the id.
- "delete": delete update_dict from the id.
Returns modified filename.
"""
split_dirs = filename.split("/")
parentDir = "/".join(split_dirs[:-1])
file = split_dirs[-1]
d = id_to_dict(file)
if mode == "add":
d.update(update_dict)
elif mode == "delete":
d = {k: v for k, v in d.items() if k not in update_dict.keys()}
var_values = [part.split("-") for part in file.split("_")]
init = "_".join([part[0] for part in var_values if len(part) == 1])
ext = os.path.splitext(file)[1]
new_filename = os.path.join(parentDir, f"{init}_{dict_to_id(d)}{ext}")
return new_filename
[docs]def id_renamer(update_dict, parentDir, key=None, mode="add"):
"""
Modifies id of files in parentDir by updating the underlying dict.
Attrs:
- update_dict: dict to use for updating the id
- parentDir: folder where files are located.
- key: string contained in the file for it to be modified.
- mode: - "add": add update_dict to the id.
- "delete": delete update_dict from the id.
Returns #modified files.
NOTE: If update_dict={} => filenames will be rearranged according to other_utils.dict_to_id.
"""
r = 0
for file in os.listdir(parentDir):
if key is None or key in file:
old_filename = os.path.join(parentDir, file)
new_filename = id_updater(old_filename, update_dict, mode=mode)
os.rename(old_filename, new_filename)
r += 1
return r