mirror of
https://github.com/beak-insights/felicity-lims.git
synced 2025-02-24 08:53:00 +08:00
241 lines
6.3 KiB
Python
241 lines
6.3 KiB
Python
from datetime import datetime, timedelta
|
|
|
|
from dateutil import parser
|
|
|
|
from core.config import settings
|
|
|
|
|
|
def get_passed_args(inspection):
|
|
"""
|
|
Retrieve user passed function arguments from the current frame from inspect
|
|
|
|
:param inspection: current frame arguments
|
|
:return: dict of arguments passed into function
|
|
usage:
|
|
import inspector
|
|
inspector = inspect.getargvalues(inspect.currentframe())
|
|
passed_args = get_passed_args(inspector)
|
|
"""
|
|
_args = inspection.args
|
|
_locals = inspection.locals
|
|
|
|
kwargs = {}
|
|
if "kwargs" in _locals.keys():
|
|
kwargs = _locals.get("kwargs")
|
|
del _locals["kwargs"]
|
|
|
|
if "self" in _args:
|
|
del _locals["self"]
|
|
|
|
if "info" in _args:
|
|
del _locals["info"]
|
|
|
|
final = {**kwargs, **_locals}
|
|
# [(arg,args.locals[arg]) for arg in args.args]
|
|
return final
|
|
|
|
|
|
def has_value_or_is_truthy(val) -> bool: # noqa
|
|
if isinstance(val, bool):
|
|
return True
|
|
|
|
if not val:
|
|
return False
|
|
|
|
if isinstance(val, str):
|
|
if not val.strip():
|
|
return False
|
|
return True
|
|
|
|
|
|
def to_text(val) -> str: # noqa
|
|
return str(val)
|
|
|
|
|
|
def get_time_now(str_format=True) -> str | datetime:
|
|
if settings.TIMEZONE_AWARE:
|
|
now = datetime.now(settings.TIMEZONE)
|
|
else:
|
|
now = datetime.now()
|
|
if str_format:
|
|
return now.strftime(settings.DATETIME_STR_FORMAT)
|
|
return now
|
|
|
|
|
|
def to_datetime(date_value: str) -> datetime:
|
|
return parser.parse(date_value)
|
|
|
|
|
|
def datetime_math(date_val: str | datetime, days: int, addition=True) -> datetime:
|
|
if isinstance(date_val, str):
|
|
date_val: datetime = to_datetime(date_val)
|
|
|
|
if addition:
|
|
return date_val + timedelta(days=days) # noqa
|
|
else:
|
|
return date_val - timedelta(days=days) # noqa
|
|
|
|
|
|
def format_datetime(
|
|
dat_value: str | datetime, human_format=False, with_time=True
|
|
) -> str:
|
|
if human_format:
|
|
if with_time:
|
|
_format = settings.DATETIME_HUMAN_FORMAT
|
|
else:
|
|
_format = settings.DATE_HUMAN_FORMAT
|
|
else:
|
|
if with_time:
|
|
_format = settings.DATETIME_STR_FORMAT
|
|
else:
|
|
_format = settings.DATE_STR_FORMAT
|
|
|
|
if isinstance(dat_value, datetime):
|
|
return dat_value.strftime(_format)
|
|
|
|
return to_datetime(dat_value).strftime(_format)
|
|
|
|
|
|
def make_tz_aware(unaware: str | datetime):
|
|
if isinstance(unaware, datetime):
|
|
return unaware.replace(tzinfo=settings.TIMEZONE)
|
|
return datetime.strptime(unaware, settings.DATETIME_STR_FORMAT).replace(
|
|
tzinfo=settings.TIMEZONE
|
|
)
|
|
|
|
|
|
def get_from_nested(obj: dict, path: str):
|
|
"""
|
|
Traversed a json/dict object tree and returns the required value if exists
|
|
:param obj: dict object to be traversed
|
|
:param path: the paths to find the required value
|
|
:return: Its return the required value
|
|
"""
|
|
if not obj:
|
|
return ""
|
|
keys = path.split(".")
|
|
key = keys.pop(0)
|
|
value = obj.get(key)
|
|
if len(keys) == 0:
|
|
return value
|
|
else:
|
|
return get_from_nested(value, ".".join(keys))
|
|
|
|
|
|
def delete_from_nested(obj: dict, path: str):
|
|
"""
|
|
Traverses a json/dict object tree and deletes the required value if exists
|
|
:param obj: dict object to be traversed
|
|
:param path: the paths to find the required value
|
|
:return: None
|
|
"""
|
|
|
|
if not obj:
|
|
return
|
|
if not path:
|
|
return obj
|
|
|
|
keys = path.split(".")
|
|
key = keys.pop(0)
|
|
value = obj.get(key)
|
|
if len(keys) == 0:
|
|
if isinstance(value, list):
|
|
for item in value:
|
|
delete_from_nested(item, ".".join(keys))
|
|
else:
|
|
del obj[key]
|
|
else:
|
|
if isinstance(value, list):
|
|
for item in value:
|
|
delete_from_nested(item, ".".join(keys))
|
|
else:
|
|
delete_from_nested(value, ".".join(keys))
|
|
|
|
|
|
def strtobool(val):
|
|
"""Convert a string representation of truth to true (1) or false (0).
|
|
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
|
|
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
|
|
'val' is anything else.
|
|
"""
|
|
val = val.lower()
|
|
if val in ("y", "yes", "t", "true", "on", "1"):
|
|
return True
|
|
elif val in ("n", "no", "f", "false", "off", "0"):
|
|
return False
|
|
else:
|
|
raise ValueError("invalid truth value %r" % (val,))
|
|
|
|
|
|
def marshaller(obj, path=None, memoize=None, exclude: list[str] = None) -> dict | str:
|
|
"""Notes:
|
|
1. We use memoization To prevent marshalling the same object again hence speed things up
|
|
2. We use path tracking To stop marshalling when a path starts to repeat itself or meets a certain path restriction
|
|
"""
|
|
if memoize is None:
|
|
memoize = {}
|
|
|
|
if path is None:
|
|
path = []
|
|
|
|
if id(obj) in memoize:
|
|
return memoize[id(obj)]
|
|
|
|
if not hasattr(obj, "__dict__"):
|
|
if obj is None:
|
|
return ""
|
|
if isinstance(obj, datetime):
|
|
return format_datetime(obj, human_format=False, with_time=True)
|
|
if hasattr(obj, "__str__"):
|
|
return obj.__str__()
|
|
return obj
|
|
|
|
result = {}
|
|
for key, val in obj.__dict__.items():
|
|
if (key.startswith("_") or key in exclude) or (path and path[-1] == key):
|
|
continue
|
|
|
|
element = []
|
|
if isinstance(val, list):
|
|
for item in val:
|
|
element.append(marshaller(item, path + [key], memoize, exclude))
|
|
else:
|
|
element = marshaller(val, path + [key], memoize, exclude)
|
|
result[key] = element
|
|
|
|
memoize[id(obj)] = result
|
|
return result
|
|
|
|
|
|
def clean_paths(obj: dict) -> dict:
|
|
paths = [
|
|
"profiles.analyses.profiles",
|
|
"analyses.profiles.analyses",
|
|
"analysis_results.analysis.profiles",
|
|
]
|
|
for _path in paths:
|
|
obj = delete_from_nested(obj, _path)
|
|
return obj
|
|
|
|
|
|
def remove_circular_refs(ob, _seen=None):
|
|
if _seen is None:
|
|
_seen = set()
|
|
|
|
if id(ob) in _seen:
|
|
return None
|
|
_seen.add(id(ob))
|
|
|
|
res = ob
|
|
|
|
if isinstance(ob, dict):
|
|
res = {
|
|
remove_circular_refs(key, _seen): remove_circular_refs(value, _seen)
|
|
for key, value in ob.items()
|
|
}
|
|
|
|
elif isinstance(ob, (list, tuple, set, frozenset)):
|
|
res = type(ob)(remove_circular_refs(v, _seen) for v in ob)
|
|
|
|
_seen.remove(id(ob))
|
|
return res
|