Coverage for tinytroupe / utils / rendering.py: 42%
57 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-28 17:48 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-28 17:48 +0000
1import json
2import textwrap
3from datetime import datetime
4from typing import Union
5import inspect
7from tinytroupe.utils import logger
10################################################################################
11# Rendering and markup
12################################################################################
13def inject_html_css_style_prefix(html, style_prefix_attributes):
14 """
15 Injects a style prefix to all style attributes in the given HTML string.
17 For example, if you want to add a style prefix to all style attributes in the HTML string
18 ``<div style="color: red;">Hello</div>``, you can use this function as follows:
19 inject_html_css_style_prefix('<div style="color: red;">Hello</div>', 'font-size: 20px;')
20 """
21 return html.replace('style="', f'style="{style_prefix_attributes};')
23def break_text_at_length(text: Union[str, dict], max_length: int=None) -> str:
24 """
25 Breaks the text (or JSON) at the specified length, inserting a "(...)" string at the break point.
26 If the maximum length is `None`, the content is returned as is.
27 """
28 if isinstance(text, dict):
29 text = json.dumps(text, indent=4)
31 if max_length is None or len(text) <= max_length:
32 return text
33 else:
34 return text[:max_length] + " (...)"
36def pretty_datetime(dt: datetime) -> str:
37 """
38 Returns a pretty string representation of the specified datetime object.
39 """
40 return dt.strftime("%Y-%m-%d %H:%M")
42def dedent(text: str) -> str:
43 """
44 Dedents the specified text, removing any leading whitespace and identation.
45 """
46 return textwrap.dedent(text).strip()
48def wrap_text(text: str, width: int=100) -> str:
49 """
50 Wraps the text at the specified width.
51 """
52 return textwrap.fill(text, width=width)
55def indent_at_current_level(text: str) -> str:
56 """
57 Indents the specified text at the current indentation level, determined dynamically.
58 """
59 frame = inspect.currentframe().f_back
60 line = frame.f_lineno
61 filename = frame.f_code.co_filename
62 with open(filename, 'r', encoding='utf-8', errors='replace') as f:
63 lines = f.readlines()
64 current_line = lines[line - 1]
66 indent= len(current_line) - len(current_line.lstrip())
68 # first dedent the text to remove any leading whitespace
69 text = dedent(text)
71 # then indent it to the specified level
72 return textwrap.indent(text, ' ' * indent)
75class RichTextStyle:
77 # Consult color options here: https://rich.readthedocs.io/en/stable/appendix/colors.html
79 STIMULUS_CONVERSATION_STYLE = "bold italic cyan1"
80 STIMULUS_THOUGHT_STYLE = "dim italic cyan1"
81 STIMULUS_DEFAULT_STYLE = "italic"
83 ACTION_DONE_STYLE = "grey82"
84 ACTION_TALK_STYLE = "bold green3"
85 ACTION_THINK_STYLE = "green"
86 ACTION_DEFAULT_STYLE = "purple"
88 INTERVENTION_DEFAULT_STYLE = "bright_magenta"
90 @classmethod
91 def get_style_for(cls, kind:str, event_type:str=None):
92 if kind == "stimulus" or kind=="stimuli":
93 if event_type == "CONVERSATION":
94 return cls.STIMULUS_CONVERSATION_STYLE
95 elif event_type == "THOUGHT":
96 return cls.STIMULUS_THOUGHT_STYLE
97 else:
98 return cls.STIMULUS_DEFAULT_STYLE
100 elif kind == "action":
101 if event_type == "DONE":
102 return cls.ACTION_DONE_STYLE
103 elif event_type == "TALK":
104 return cls.ACTION_TALK_STYLE
105 elif event_type == "THINK":
106 return cls.ACTION_THINK_STYLE
107 else:
108 return cls.ACTION_DEFAULT_STYLE
110 elif kind == "intervention":
111 return cls.INTERVENTION_DEFAULT_STYLE