Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modules.auto: allow using properties in the formatting string #46

Merged
merged 1 commit into from
Dec 24, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions src/orger/modules/auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
from orger.inorganic import node, link, Quoted
from orger.common import dt_heading, error

from typing import Optional, List, Iterator, Any
from datetime import datetime
from typing import Optional, List, Iterator, Any
from pprint import pformat
import string

from more_itertools import bucket

from my.core.common import asdict
from pprint import pformat


def pp_item(i, **kwargs) -> str:
# annoying, pprint doesn't have dataclass support till 3.10 https://bugs.python.org/issue43080
return pformat(asdict(i), **kwargs)
Expand Down Expand Up @@ -81,60 +84,61 @@ def _warn(self, w: str):
self.extra_warnings.append(w)


def render_one(self, i) -> Iterator[node]:
def render_one(self, thing) -> Iterator[node]:
self._warn('WARNING: Default renderer is used! Implement render_one if you want nicer rendering')

d = asdict(i)
cls = i.__class__
del i # delete from scope to avoid using by accident
thing_dict = asdict(thing)
cls = thing.__class__

datetimes = [(k, v) for k, v in d.items() if isinstance(v, datetime)]
datetimes = [(k, v) for k, v in thing_dict.items() if isinstance(v, datetime)]
dt: Optional[datetime] = None
if len(datetimes) == 1:
[(k, dt)] = datetimes
# todo maybe warn that datetime is guessed
del d[k] # probs no need to press twice?
del thing_dict[k] # probs no need to press twice?
self._warn(f"NOTE: {cls} is using '{k}' as the timestamp")
else:
self._warn(f"WARNING: {cls} couldn't guess timestamp: expected single datetime field, got {datetimes}")

# todo could pass fmt string for group as well?
if self.group_by_attr is not None:
try:
d.pop(self.group_by_attr)
thing_dict.pop(self.group_by_attr)
except KeyError as e:
yield error(e)

def fmt_attr(attr: Optional[str]) -> Optional[str]:
if attr is None:
return None

import string
fake_self: Any = object() # fine to call it as class method here..
fields = [
f
for (_, f, _, _) in string.Formatter.parse(fake_self, attr)
if f is not None # might be none for the last bit (before the static bit)
if f is not None # might be none for the last bit (before the static bit)
]
fstr: str
if len(fields) == 0:
# must be field name without formatting string?
# TODO maybe deprecate this.. it's not a huge deal to pass extra curlies
fields = [attr]
fstr = '{' + attr + '}'
else:
fstr = attr
res = fstr.format(**d)

# here we can't use the thing_dict since formatter string might refer to properties
# TODO maybe use freezer instead?... or dump to dict with properties
res = fstr.format(**{f: getattr(thing, f) for f in fields})
for f in fields:
# should succeed after format call?
d.pop(f)
thing_dict.pop(f, None) # might not exist if it was a @property
return res


title = fmt_attr(self.title_attr)
body = fmt_attr(self.body_attr)

node_title = cls.__name__ if title is None else title
node_body = Quoted(pp_item(d, width=120)).quoted()
node_body = Quoted(pp_item(thing_dict, width=120)).quoted()
if body is not None:
node_body += '\n' + body

Expand Down