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

1import json 

2import textwrap 

3from datetime import datetime 

4from typing import Union 

5import inspect 

6 

7from tinytroupe.utils import logger 

8 

9 

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. 

16 

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};') 

22 

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) 

30 

31 if max_length is None or len(text) <= max_length: 

32 return text 

33 else: 

34 return text[:max_length] + " (...)" 

35 

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") 

41 

42def dedent(text: str) -> str: 

43 """ 

44 Dedents the specified text, removing any leading whitespace and identation. 

45 """ 

46 return textwrap.dedent(text).strip() 

47 

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) 

53 

54 

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] 

65 

66 indent= len(current_line) - len(current_line.lstrip()) 

67 

68 # first dedent the text to remove any leading whitespace 

69 text = dedent(text) 

70 

71 # then indent it to the specified level 

72 return textwrap.indent(text, ' ' * indent) 

73 

74 

75class RichTextStyle: 

76 

77 # Consult color options here: https://rich.readthedocs.io/en/stable/appendix/colors.html 

78 

79 STIMULUS_CONVERSATION_STYLE = "bold italic cyan1" 

80 STIMULUS_THOUGHT_STYLE = "dim italic cyan1" 

81 STIMULUS_DEFAULT_STYLE = "italic" 

82 

83 ACTION_DONE_STYLE = "grey82" 

84 ACTION_TALK_STYLE = "bold green3" 

85 ACTION_THINK_STYLE = "green" 

86 ACTION_DEFAULT_STYLE = "purple" 

87 

88 INTERVENTION_DEFAULT_STYLE = "bright_magenta" 

89 

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 

99 

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 

109 

110 elif kind == "intervention": 

111 return cls.INTERVENTION_DEFAULT_STYLE 

112