from datetime import datetime from math import ceil import os from os.path import isfile, dirname import traceback class AppError(Exception): pass def open_file(fn): d = dirname(fn) os.makedirs(d, exist_ok=True) if isfile(fn): return open(fn, 'rb+') else: return open(fn, 'ab+') def must_get(d, k): if type(k) == str: k = k.split('.') part = k.pop(0) if type(d) != dict or part not in d: raise ValueError(part + ' not found') val = d[part] if len(k) == 0: return val else: return must_get(val, k) mg = must_get def should_get(d, k, default=None): try: return must_get(d, k) except ValueError: return default sg = should_get def find_where_eq(l, k, v): for i in l: if k in i and i[k] == v: return i return None def all_subclasses(cls): return set(cls.__subclasses__()).union( [s for c in cls.__subclasses__() for s in all_subclasses(c)]) def pprint(d, detail=2): print(pretty(d, detail=detail)) def pretty(d, ident=0, detail=2): if type(d) in [int, str, float, bool]: return str(d) if hasattr(d, 'f'): return d.f(detail) r = '' idt = ' ' * ident if type(d) in [list, set]: r += 'lst' for i in d: r += '\n' + idt + pretty(i, ident + 1, detail) elif type(d) == dict: r += 'map' for k,v in d.items(): r += '\n' + idt + k + ': ' + pretty(v, ident + 1, detail) return r def trim(s, l): s = s[:l] s += ' ' * (l-len(s)) return s def total_pages(meta): total = mg(meta, 'total') limit = mg(meta, 'limit') pages = ceil(total / limit) return pages # >>> parse_timestamp('2023-06-02T20:34:48.293Z') # 1685738088 def parse_timestamp(ts): return int(datetime.strptime(ts, '%Y-%m-%dT%H:%M:%S.%f%z').timestamp()) def render_timestamp(ts): return datetime.utcfromtimestamp(ts).isoformat() def fmtex(e): return ''.join(traceback.TracebackException.from_exception(e).format())