edsl 0.1.38.dev2__py3-none-any.whl → 0.1.38.dev3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. edsl/Base.py +303 -303
  2. edsl/BaseDiff.py +260 -260
  3. edsl/TemplateLoader.py +24 -24
  4. edsl/__init__.py +49 -49
  5. edsl/__version__.py +1 -1
  6. edsl/agents/Agent.py +858 -858
  7. edsl/agents/AgentList.py +362 -362
  8. edsl/agents/Invigilator.py +222 -222
  9. edsl/agents/InvigilatorBase.py +284 -284
  10. edsl/agents/PromptConstructor.py +353 -353
  11. edsl/agents/__init__.py +3 -3
  12. edsl/agents/descriptors.py +99 -99
  13. edsl/agents/prompt_helpers.py +129 -129
  14. edsl/auto/AutoStudy.py +117 -117
  15. edsl/auto/StageBase.py +230 -230
  16. edsl/auto/StageGenerateSurvey.py +178 -178
  17. edsl/auto/StageLabelQuestions.py +125 -125
  18. edsl/auto/StagePersona.py +61 -61
  19. edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
  20. edsl/auto/StagePersonaDimensionValues.py +74 -74
  21. edsl/auto/StagePersonaDimensions.py +69 -69
  22. edsl/auto/StageQuestions.py +73 -73
  23. edsl/auto/SurveyCreatorPipeline.py +21 -21
  24. edsl/auto/utilities.py +224 -224
  25. edsl/base/Base.py +279 -279
  26. edsl/config.py +149 -149
  27. edsl/conversation/Conversation.py +290 -290
  28. edsl/conversation/car_buying.py +58 -58
  29. edsl/conversation/chips.py +95 -95
  30. edsl/conversation/mug_negotiation.py +81 -81
  31. edsl/conversation/next_speaker_utilities.py +93 -93
  32. edsl/coop/PriceFetcher.py +54 -54
  33. edsl/coop/__init__.py +2 -2
  34. edsl/coop/coop.py +961 -961
  35. edsl/coop/utils.py +131 -131
  36. edsl/data/Cache.py +530 -530
  37. edsl/data/CacheEntry.py +228 -228
  38. edsl/data/CacheHandler.py +149 -149
  39. edsl/data/RemoteCacheSync.py +97 -97
  40. edsl/data/SQLiteDict.py +292 -292
  41. edsl/data/__init__.py +4 -4
  42. edsl/data/orm.py +10 -10
  43. edsl/data_transfer_models.py +73 -73
  44. edsl/enums.py +173 -173
  45. edsl/exceptions/BaseException.py +21 -21
  46. edsl/exceptions/__init__.py +54 -54
  47. edsl/exceptions/agents.py +42 -42
  48. edsl/exceptions/cache.py +5 -5
  49. edsl/exceptions/configuration.py +16 -16
  50. edsl/exceptions/coop.py +10 -10
  51. edsl/exceptions/data.py +14 -14
  52. edsl/exceptions/general.py +34 -34
  53. edsl/exceptions/jobs.py +33 -33
  54. edsl/exceptions/language_models.py +63 -63
  55. edsl/exceptions/prompts.py +15 -15
  56. edsl/exceptions/questions.py +91 -91
  57. edsl/exceptions/results.py +29 -29
  58. edsl/exceptions/scenarios.py +22 -22
  59. edsl/exceptions/surveys.py +37 -37
  60. edsl/inference_services/AnthropicService.py +87 -87
  61. edsl/inference_services/AwsBedrock.py +120 -120
  62. edsl/inference_services/AzureAI.py +217 -217
  63. edsl/inference_services/DeepInfraService.py +18 -18
  64. edsl/inference_services/GoogleService.py +156 -156
  65. edsl/inference_services/GroqService.py +20 -20
  66. edsl/inference_services/InferenceServiceABC.py +147 -147
  67. edsl/inference_services/InferenceServicesCollection.py +97 -97
  68. edsl/inference_services/MistralAIService.py +123 -123
  69. edsl/inference_services/OllamaService.py +18 -18
  70. edsl/inference_services/OpenAIService.py +224 -224
  71. edsl/inference_services/TestService.py +89 -89
  72. edsl/inference_services/TogetherAIService.py +170 -170
  73. edsl/inference_services/models_available_cache.py +118 -118
  74. edsl/inference_services/rate_limits_cache.py +25 -25
  75. edsl/inference_services/registry.py +39 -39
  76. edsl/inference_services/write_available.py +10 -10
  77. edsl/jobs/Answers.py +56 -56
  78. edsl/jobs/Jobs.py +1358 -1358
  79. edsl/jobs/__init__.py +1 -1
  80. edsl/jobs/buckets/BucketCollection.py +63 -63
  81. edsl/jobs/buckets/ModelBuckets.py +65 -65
  82. edsl/jobs/buckets/TokenBucket.py +251 -251
  83. edsl/jobs/interviews/Interview.py +661 -661
  84. edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
  85. edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
  86. edsl/jobs/interviews/InterviewStatistic.py +63 -63
  87. edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
  88. edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
  89. edsl/jobs/interviews/InterviewStatusLog.py +92 -92
  90. edsl/jobs/interviews/ReportErrors.py +66 -66
  91. edsl/jobs/interviews/interview_status_enum.py +9 -9
  92. edsl/jobs/runners/JobsRunnerAsyncio.py +361 -361
  93. edsl/jobs/runners/JobsRunnerStatus.py +332 -332
  94. edsl/jobs/tasks/QuestionTaskCreator.py +242 -242
  95. edsl/jobs/tasks/TaskCreators.py +64 -64
  96. edsl/jobs/tasks/TaskHistory.py +451 -451
  97. edsl/jobs/tasks/TaskStatusLog.py +23 -23
  98. edsl/jobs/tasks/task_status_enum.py +163 -163
  99. edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
  100. edsl/jobs/tokens/TokenUsage.py +34 -34
  101. edsl/language_models/KeyLookup.py +30 -30
  102. edsl/language_models/LanguageModel.py +708 -708
  103. edsl/language_models/ModelList.py +109 -109
  104. edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
  105. edsl/language_models/__init__.py +3 -3
  106. edsl/language_models/fake_openai_call.py +15 -15
  107. edsl/language_models/fake_openai_service.py +61 -61
  108. edsl/language_models/registry.py +137 -137
  109. edsl/language_models/repair.py +156 -156
  110. edsl/language_models/unused/ReplicateBase.py +83 -83
  111. edsl/language_models/utilities.py +64 -64
  112. edsl/notebooks/Notebook.py +258 -258
  113. edsl/notebooks/__init__.py +1 -1
  114. edsl/prompts/Prompt.py +357 -357
  115. edsl/prompts/__init__.py +2 -2
  116. edsl/questions/AnswerValidatorMixin.py +289 -289
  117. edsl/questions/QuestionBase.py +660 -660
  118. edsl/questions/QuestionBaseGenMixin.py +161 -161
  119. edsl/questions/QuestionBasePromptsMixin.py +217 -217
  120. edsl/questions/QuestionBudget.py +227 -227
  121. edsl/questions/QuestionCheckBox.py +359 -359
  122. edsl/questions/QuestionExtract.py +183 -183
  123. edsl/questions/QuestionFreeText.py +114 -114
  124. edsl/questions/QuestionFunctional.py +166 -166
  125. edsl/questions/QuestionList.py +231 -231
  126. edsl/questions/QuestionMultipleChoice.py +286 -286
  127. edsl/questions/QuestionNumerical.py +153 -153
  128. edsl/questions/QuestionRank.py +324 -324
  129. edsl/questions/Quick.py +41 -41
  130. edsl/questions/RegisterQuestionsMeta.py +71 -71
  131. edsl/questions/ResponseValidatorABC.py +174 -174
  132. edsl/questions/SimpleAskMixin.py +73 -73
  133. edsl/questions/__init__.py +26 -26
  134. edsl/questions/compose_questions.py +98 -98
  135. edsl/questions/decorators.py +21 -21
  136. edsl/questions/derived/QuestionLikertFive.py +76 -76
  137. edsl/questions/derived/QuestionLinearScale.py +87 -87
  138. edsl/questions/derived/QuestionTopK.py +93 -93
  139. edsl/questions/derived/QuestionYesNo.py +82 -82
  140. edsl/questions/descriptors.py +413 -413
  141. edsl/questions/prompt_templates/question_budget.jinja +13 -13
  142. edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
  143. edsl/questions/prompt_templates/question_extract.jinja +11 -11
  144. edsl/questions/prompt_templates/question_free_text.jinja +3 -3
  145. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
  146. edsl/questions/prompt_templates/question_list.jinja +17 -17
  147. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
  148. edsl/questions/prompt_templates/question_numerical.jinja +36 -36
  149. edsl/questions/question_registry.py +147 -147
  150. edsl/questions/settings.py +12 -12
  151. edsl/questions/templates/budget/answering_instructions.jinja +7 -7
  152. edsl/questions/templates/budget/question_presentation.jinja +7 -7
  153. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
  154. edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
  155. edsl/questions/templates/extract/answering_instructions.jinja +7 -7
  156. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
  157. edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
  158. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
  159. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
  160. edsl/questions/templates/list/answering_instructions.jinja +3 -3
  161. edsl/questions/templates/list/question_presentation.jinja +5 -5
  162. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
  163. edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
  164. edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
  165. edsl/questions/templates/numerical/question_presentation.jinja +6 -6
  166. edsl/questions/templates/rank/answering_instructions.jinja +11 -11
  167. edsl/questions/templates/rank/question_presentation.jinja +15 -15
  168. edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
  169. edsl/questions/templates/top_k/question_presentation.jinja +22 -22
  170. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
  171. edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
  172. edsl/results/Dataset.py +293 -293
  173. edsl/results/DatasetExportMixin.py +717 -717
  174. edsl/results/DatasetTree.py +145 -145
  175. edsl/results/Result.py +456 -456
  176. edsl/results/Results.py +1071 -1071
  177. edsl/results/ResultsDBMixin.py +238 -238
  178. edsl/results/ResultsExportMixin.py +43 -43
  179. edsl/results/ResultsFetchMixin.py +33 -33
  180. edsl/results/ResultsGGMixin.py +121 -121
  181. edsl/results/ResultsToolsMixin.py +98 -98
  182. edsl/results/Selector.py +135 -135
  183. edsl/results/__init__.py +2 -2
  184. edsl/results/tree_explore.py +115 -115
  185. edsl/scenarios/FileStore.py +458 -458
  186. edsl/scenarios/Scenario.py +544 -544
  187. edsl/scenarios/ScenarioHtmlMixin.py +64 -64
  188. edsl/scenarios/ScenarioList.py +1112 -1112
  189. edsl/scenarios/ScenarioListExportMixin.py +52 -52
  190. edsl/scenarios/ScenarioListPdfMixin.py +261 -261
  191. edsl/scenarios/__init__.py +4 -4
  192. edsl/shared.py +1 -1
  193. edsl/study/ObjectEntry.py +173 -173
  194. edsl/study/ProofOfWork.py +113 -113
  195. edsl/study/SnapShot.py +80 -80
  196. edsl/study/Study.py +528 -528
  197. edsl/study/__init__.py +4 -4
  198. edsl/surveys/DAG.py +148 -148
  199. edsl/surveys/Memory.py +31 -31
  200. edsl/surveys/MemoryPlan.py +244 -244
  201. edsl/surveys/Rule.py +326 -326
  202. edsl/surveys/RuleCollection.py +387 -387
  203. edsl/surveys/Survey.py +1787 -1787
  204. edsl/surveys/SurveyCSS.py +261 -261
  205. edsl/surveys/SurveyExportMixin.py +259 -259
  206. edsl/surveys/SurveyFlowVisualizationMixin.py +121 -121
  207. edsl/surveys/SurveyQualtricsImport.py +284 -284
  208. edsl/surveys/__init__.py +3 -3
  209. edsl/surveys/base.py +53 -53
  210. edsl/surveys/descriptors.py +56 -56
  211. edsl/surveys/instructions/ChangeInstruction.py +49 -49
  212. edsl/surveys/instructions/Instruction.py +53 -53
  213. edsl/surveys/instructions/InstructionCollection.py +77 -77
  214. edsl/templates/error_reporting/base.html +23 -23
  215. edsl/templates/error_reporting/exceptions_by_model.html +34 -34
  216. edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
  217. edsl/templates/error_reporting/exceptions_by_type.html +16 -16
  218. edsl/templates/error_reporting/interview_details.html +115 -115
  219. edsl/templates/error_reporting/interviews.html +9 -9
  220. edsl/templates/error_reporting/overview.html +4 -4
  221. edsl/templates/error_reporting/performance_plot.html +1 -1
  222. edsl/templates/error_reporting/report.css +73 -73
  223. edsl/templates/error_reporting/report.html +117 -117
  224. edsl/templates/error_reporting/report.js +25 -25
  225. edsl/tools/__init__.py +1 -1
  226. edsl/tools/clusters.py +192 -192
  227. edsl/tools/embeddings.py +27 -27
  228. edsl/tools/embeddings_plotting.py +118 -118
  229. edsl/tools/plotting.py +112 -112
  230. edsl/tools/summarize.py +18 -18
  231. edsl/utilities/SystemInfo.py +28 -28
  232. edsl/utilities/__init__.py +22 -22
  233. edsl/utilities/ast_utilities.py +25 -25
  234. edsl/utilities/data/Registry.py +6 -6
  235. edsl/utilities/data/__init__.py +1 -1
  236. edsl/utilities/data/scooter_results.json +1 -1
  237. edsl/utilities/decorators.py +77 -77
  238. edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
  239. edsl/utilities/interface.py +627 -627
  240. edsl/utilities/naming_utilities.py +263 -263
  241. edsl/utilities/repair_functions.py +28 -28
  242. edsl/utilities/restricted_python.py +70 -70
  243. edsl/utilities/utilities.py +409 -409
  244. {edsl-0.1.38.dev2.dist-info → edsl-0.1.38.dev3.dist-info}/LICENSE +21 -21
  245. {edsl-0.1.38.dev2.dist-info → edsl-0.1.38.dev3.dist-info}/METADATA +1 -1
  246. edsl-0.1.38.dev3.dist-info/RECORD +269 -0
  247. edsl-0.1.38.dev2.dist-info/RECORD +0 -269
  248. {edsl-0.1.38.dev2.dist-info → edsl-0.1.38.dev3.dist-info}/WHEEL +0 -0
@@ -1,627 +1,627 @@
1
- """A module for displaying data in various formats."""
2
-
3
- from html import escape
4
-
5
-
6
- def create_image(console, image_filename):
7
- """Create an image from the console output."""
8
- font_size = 15
9
- from PIL import Image, ImageDraw, ImageFont
10
-
11
- text = console.export_text() # Get the console output as text.
12
-
13
- # Create an image from the text
14
- font_size = 15
15
- font = ImageFont.load_default() # Use the default font to avoid file path issues.
16
- # text_width, text_height = ImageDraw.Draw(
17
- # Image.new("RGB", (100, 100))
18
- # ).multiline_textsize(text, font=font)
19
- text_width, text_height = get_multiline_textsize(text, font)
20
- image = Image.new(
21
- "RGB", (text_width + 20, text_height + 20), color=(255, 255, 255)
22
- ) # Add some padding
23
- d = ImageDraw.Draw(image)
24
-
25
- # Draw text to image
26
- d.multiline_text((10, 10), text, font=font, fill=(0, 0, 0))
27
- # Save the image
28
- image.save(image_filename)
29
-
30
-
31
- def display_table(console, table, filename):
32
- # from rich.console import Console
33
- # from rich.table import Table
34
- """Display the table using the rich library and save it to a file if a filename is provided."""
35
- if filename is not None:
36
- with open(filename, "w") as f:
37
- with console.capture() as capture:
38
- console.print(table)
39
- f.write(capture.get())
40
- create_image(console, filename + ".png")
41
- else:
42
- console.print(table)
43
-
44
-
45
- def gen_html_sandwich(html_inner, interactive=False):
46
- """Wrap the inner HTML content in a header and footer to make a complete HTML document."""
47
- return html_inner
48
- if interactive:
49
- html_header = """
50
- <html>
51
- <head>
52
- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
53
- <link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.css" />
54
- <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.js"></script>
55
- <style>
56
- table {
57
- font-family: Arial, sans-serif;
58
- border-collapse: collapse;
59
- width: 100%;
60
- }
61
-
62
- td, th {
63
- border: 1px solid #dddddd;
64
- text-align: left;
65
- padding: 8px;
66
- }
67
-
68
- tr:nth-child(even) {
69
- background-color: #dddddd;
70
- }
71
- </style>
72
- <script>
73
- $(document).ready( function () {
74
- $('#myTable').DataTable();
75
- } )
76
- </script>
77
- </head>
78
- <body>
79
- """
80
- else:
81
- html_header = """
82
- <html>
83
- <head>
84
- <style>
85
- table {
86
- font-family: Arial, sans-serif;
87
- border-collapse: collapse;
88
- width: 100%;
89
- }
90
-
91
- td, th {
92
- border: 1px solid #dddddd;
93
- text-align: left;
94
- padding: 8px;
95
- }
96
-
97
- tr:nth-child(even) {
98
- background-color: #dddddd;
99
- }
100
- </style>
101
- </head>
102
- <body>
103
- """
104
-
105
- html_footer = """
106
- </body>
107
- </html>
108
- """
109
- return html_header + html_inner + html_footer
110
-
111
-
112
- def view_html(html):
113
- """Display HTML content in a web browser."""
114
- import tempfile
115
- import webbrowser
116
-
117
- with tempfile.NamedTemporaryFile("w", delete=False, suffix=".html") as f:
118
- url = "file://" + f.name
119
- # Write the HTML content to the file
120
- f.write(html)
121
-
122
- # Open the URL in the web browser
123
- webbrowser.open(url)
124
-
125
-
126
- def human_readable_labeler_creator():
127
- """Create a function that maps thread ids to human-readable labels.
128
-
129
- It is structured as a closure, so that the mapping is persistent.
130
- I.e., when the returned function is called, it will use the same
131
- dictionary to map thread ids to human-readable labels if it's seen that ID
132
- before; otherwise, it will add a new entry to the dictionary.
133
- This will persist across calls to the function.
134
- """
135
- d = {}
136
-
137
- def func(thread_id):
138
- if thread_id in d:
139
- return d[thread_id]
140
- else:
141
- d[thread_id] = len(d)
142
- return d[thread_id]
143
-
144
- return func
145
-
146
-
147
- def get_multiline_textsize(text, font):
148
- """Get the size of the text when it is drawn on an image."""
149
- lines = text.split("\n")
150
-
151
- # Initialize width and height
152
- max_width = 0
153
- total_height = 0
154
-
155
- for line in lines:
156
- # Get the size of the text for the line
157
- box = font.getbbox(line)
158
- width, height = box[2], box[3]
159
-
160
- # Update max_width if width of the current line is greater than max_width
161
- max_width = max(max_width, width)
162
-
163
- # Add height to total_height
164
- total_height += height
165
-
166
- return max_width, total_height
167
-
168
-
169
- def print_results_long(results, max_rows=None):
170
- from rich.console import Console
171
- from rich.table import Table
172
-
173
- console = Console(record=True)
174
- table = Table(show_header=True, header_style="bold magenta")
175
- table.add_column("Result index", style="dim")
176
- table.add_column("Key", style="dim")
177
- table.add_column("Value", style="dim")
178
- list_of_dicts = results.to_dicts()
179
- num_rows = 0
180
- for i, results_dict in enumerate(list_of_dicts):
181
- for key, value in results_dict.items():
182
- table.add_row(str(i), key, str(value))
183
- num_rows += 1
184
- if max_rows is not None and num_rows >= max_rows:
185
- break
186
- console.print(table)
187
-
188
-
189
- def print_dict_with_rich(d, key_name="Key", value_name="Value", filename=None):
190
- """Print a dictionary as a table using the rich library.
191
-
192
- Example:
193
- >>> print_dict_with_rich({"a": 1, "b": 2, "c": 3})
194
- ┏━━━━━┳━━━━━━━┓
195
- ┃ Key ┃ Value ┃
196
- ┡━━━━━╇━━━━━━━┩
197
- │ a │ 1 │
198
- │ b │ 2 │
199
- │ c │ 3 │
200
- └─────┴───────┘
201
- """
202
- from rich.console import Console
203
- from rich.table import Table
204
-
205
- console = Console(record=True)
206
- table = Table(show_header=True, header_style="bold magenta")
207
- table.add_column(key_name, style="dim")
208
- table.add_column(value_name, style="dim")
209
- for key, value in d.items():
210
- table.add_row(key, str(value))
211
- console.print(table)
212
- # display_table(console, table, filename)
213
-
214
-
215
- def print_dict_as_html_table(
216
- d,
217
- show=False,
218
- key_name="Key",
219
- value_name="Value",
220
- filename=None,
221
- ):
222
- """Print a dictionary as an HTML table.
223
-
224
- :param d: The dictionary to print.
225
- :param show: Whether to display the HTML table in the browser.
226
- :param key_name: The name of the key column.
227
- :param value_name: The name of the value column.
228
- :param filename: The name of the file to save the HTML table to.
229
- """
230
- # Start the HTML table
231
- html_table = f'<table border="1">\n<tr><th>{escape(key_name)}</th><th>{escape(value_name)}</th></tr>\n'
232
-
233
- # Add rows to the HTML table
234
- for key, value in d.items():
235
- html_table += (
236
- f"<tr><td>{escape(str(key))}</td><td>{escape(str(value))}</td></tr>\n"
237
- )
238
-
239
- # Close the HTML table
240
- html_table += "</table>"
241
-
242
- # Print the HTML table to console
243
- # print(html_table)
244
-
245
- # Write to file if a filename is provided
246
- if filename:
247
- with open(filename, "w") as file:
248
- file.write(html_table)
249
- else:
250
- if show:
251
- view_html(gen_html_sandwich(html_table))
252
- else:
253
- return html_table
254
-
255
-
256
- def print_scenario_list(data):
257
- from rich.console import Console
258
- from rich.table import Table
259
-
260
- new_data = []
261
- for obs in data:
262
- try:
263
- _ = obs.pop("edsl_version")
264
- _ = obs.pop("edsl_class_name")
265
- except KeyError as e:
266
- # print(e)
267
- pass
268
- new_data.append(obs)
269
-
270
- columns = list(new_data[0].keys())
271
- console = Console(record=True)
272
-
273
- # Create a table object
274
- table = Table(show_header=True, header_style="bold magenta", show_lines=True)
275
- for column in columns:
276
- table.add_column(column, style="dim")
277
-
278
- for obs in new_data:
279
- row = [str(obs[key]) for key in columns]
280
- table.add_row(*row)
281
-
282
- console.print(table)
283
-
284
-
285
- def print_list_of_dicts_with_rich(data, filename=None, split_at_dot=True):
286
- raise Exception(
287
- "print_list_of_dicts_with_rich is now called print_dataset_with_rich"
288
- )
289
-
290
-
291
- def print_dataset_with_rich(data, filename=None, split_at_dot=True):
292
- """Initialize console object."""
293
- """
294
- The list seems superfluous.
295
- This prints a list of dictionaries as a table using the rich library.
296
-
297
- >>> data = [{"a": [1, 2, 3], "b": [4, 5, 6]}]
298
- >>> print_list_of_dicts_with_rich(data)
299
- ┏━━━┳━━━┓
300
- ┃ a ┃ b ┃
301
- ┡━━━╇━━━┩
302
- │ 1 │ 4 │
303
- ├───┼───┤
304
- │ 2 │ 5 │
305
- ├───┼───┤
306
- │ 3 │ 6 │
307
- └───┴───┘
308
- """
309
- from rich.console import Console
310
- from rich.table import Table
311
-
312
- console = Console(record=True)
313
-
314
- # Create a table object
315
- table = Table(show_header=True, header_style="bold magenta", show_lines=True)
316
-
317
- # Adding columns to the table
318
- for d in data:
319
- for key in d.keys():
320
- if split_at_dot:
321
- value = key.replace(".", "\n.")
322
- else:
323
- value = key
324
- table.add_column(value, style="dim")
325
-
326
- # Adding rows to the table
327
- num_rows = len(next(iter(data[0].values())))
328
- for i in range(num_rows):
329
- row = [str(d[key][i]) for d in data for key in d.keys()]
330
- table.add_row(*row)
331
-
332
- console.print(table)
333
- # display_table(console, table, filename)
334
-
335
-
336
- def create_latex_table_from_data(data, filename=None, split_at_dot=True):
337
- """
338
- This function takes a list of dictionaries and returns a LaTeX table as a string.
339
- The table can either be printed or written to a file.
340
-
341
- >>> data = [{"a": [1, 2, 3], "b": [4, 5, 6]}]
342
- >>> print(create_latex_table_from_data(data))
343
- \\begin{tabular}{|c|c|}
344
- \\hline
345
- a & b \\\\
346
- \\hline
347
- 1 & 4 \\\\
348
- 2 & 5 \\\\
349
- 3 & 6 \\\\
350
- \\hline
351
- \\end{tabular}
352
- """
353
-
354
- def escape_latex(s):
355
- replacements = [
356
- ("_", r"\_"),
357
- ("&", r"\&"),
358
- ("%", r"\%"),
359
- ("$", r"\$"),
360
- ("#", r"\#"),
361
- ("{", r"\{"),
362
- ("}", r"\}"),
363
- ("~", r"\textasciitilde{}"),
364
- ("^", r"\textasciicircum{}"),
365
- ("\\", r"\textbackslash{}"),
366
- ]
367
-
368
- for old, new in replacements:
369
- s = s.replace(old, new)
370
- return s
371
-
372
- # Start the LaTeX table
373
- latex_table = ["\\begin{tabular}{|" + "c|" * len(data[0]) + "}"]
374
- latex_table.append("\\hline")
375
-
376
- # Add the header row
377
- headers = []
378
- for key in data[0].keys():
379
- if split_at_dot:
380
- value = key.replace(".", "\n.")
381
- else:
382
- value = key
383
- headers.append(escape_latex(value))
384
- latex_table.append(" & ".join(headers) + " \\\\")
385
- latex_table.append("\\hline")
386
-
387
- # Determine the number of rows
388
- num_rows = len(next(iter(data[0].values())))
389
-
390
- # Debugging: Print the keys of the dictionaries
391
- # print("Keys in data[0]:", list(data[0].keys()))
392
-
393
- # Add the data rows
394
- for i in range(num_rows):
395
- row = []
396
- for key in data[0].keys():
397
- for d in data:
398
- try:
399
- row.append(escape_latex(str(d[key][i])))
400
- except KeyError as e:
401
- print(
402
- f"KeyError: {e} - Key '{key}' not found in data dictionary. The keys are {list(d.keys())}"
403
- )
404
- raise
405
- latex_table.append(" & ".join(row) + " \\\\")
406
-
407
- latex_table.append("\\hline")
408
- latex_table.append("\\end{tabular}")
409
-
410
- # Join all parts into a single string
411
- latex_table_str = "\n".join(latex_table)
412
-
413
- # Write to file if filename is provided
414
- if filename:
415
- with open(filename, "w") as f:
416
- f.write(latex_table_str)
417
- print(f"Table written to {filename}")
418
-
419
- return latex_table_str
420
-
421
-
422
- def print_list_of_dicts_as_html_table(data, interactive=True):
423
- """Print a list of dictionaries as an HTML table.
424
-
425
- :param data: The list of dictionaries to print.
426
- :param filename: The name of the file to save the HTML table to.
427
- :param interactive: Whether to make the table interactive using DataTables.
428
- """
429
- style = """
430
- <style>
431
- table {
432
- width: 100%;
433
- border-collapse: collapse;
434
- }
435
- table, th, td {
436
- border: 1px solid black;
437
- }
438
- th, td {
439
- padding: 10px;
440
- text-align: left;
441
- }
442
- </style>
443
- """
444
- html_table = style + '<table id="myTable" class="display">\n'
445
- html_table += " <thead>\n"
446
- # Add the header row
447
- headers = [key for d in data for key in d.keys()]
448
- html_table += " <tr>\n"
449
- for header in headers:
450
- html_table += f" <th>{header}</th>\n"
451
- html_table += " </tr>\n"
452
- html_table += " </thead>\n</tbody>\n"
453
-
454
- # Determine the number of rows
455
- num_rows = max(len(values) for d in data for values in d.values())
456
-
457
- # Add the data rows
458
- for i in range(num_rows):
459
- html_table += " <tr>\n"
460
- for d in data:
461
- for key in d.keys():
462
- value = d[key][i] if i < len(d[key]) else ""
463
- html_table += f" <td>{value}</td>\n"
464
- html_table += " </tr>\n"
465
-
466
- # Close the table
467
- html_table += "</tbody>\n"
468
- html_table += "</table>"
469
- return gen_html_sandwich(html_table, interactive=interactive)
470
-
471
-
472
- def print_list_of_dicts_as_markdown_table(data, filename=None):
473
- """Print a list of dictionaries as a Markdown table.
474
-
475
- :param data: The list of dictionaries to print.
476
- :param filename: The name of the file to save the Markdown table to.
477
- """
478
- if not data:
479
- print("No data provided")
480
- return
481
-
482
- # Gather all unique headers
483
- # headers = list({key for d in data for key in d.keys()})
484
- headers = []
485
- for column in data:
486
- headers.append(list(column.keys())[0])
487
-
488
- markdown_table = "| " + " | ".join(headers) + " |\n"
489
- markdown_table += "|-" + "-|-".join(["" for _ in headers]) + "-|\n"
490
-
491
- num_rows = len(next(iter(data[0].values())))
492
- for i in range(num_rows):
493
- row = [str(d[key][i]) for d in data for key in d.keys()]
494
- # table.add_row(*row)
495
- markdown_table += "| " + " | ".join(row) + " |\n"
496
-
497
- # Output or save to file
498
- if filename:
499
- with open(filename, "w") as f:
500
- f.write(markdown_table)
501
- else:
502
- print(markdown_table)
503
-
504
-
505
- def print_public_methods_with_doc(obj):
506
- """Print the public methods of an object along with their docstrings."""
507
- from rich.console import Console
508
- from rich.table import Table
509
-
510
- console = Console()
511
- public_methods_with_docstrings = [
512
- (method, getattr(obj, method).__doc__)
513
- for method in dir(obj)
514
- if callable(getattr(obj, method))
515
- and not method.startswith("_")
516
- and method != "methods"
517
- ]
518
-
519
- for method, doc in public_methods_with_docstrings:
520
- if doc:
521
- console.print(f"[bold]{method}:[/bold]", style="green")
522
- console.print(f"\t{doc.strip()}", style="yellow")
523
-
524
-
525
- def print_tally_with_rich(data, filename=None):
526
- """Print a tally of values in a list using the rich library.
527
-
528
- Example:
529
- >>> data = {'a':12, 'b':14, 'c':9}
530
- >>> print_tally_with_rich(data)
531
- ┏━━━━━━━┳━━━━━━━┓
532
- ┃ Value ┃ Count ┃
533
- ┡━━━━━━━╇━━━━━━━┩
534
- │ a │ 12 │
535
- │ b │ 14 │
536
- │ c │ 9 │
537
- └───────┴───────┘
538
- """
539
- # Initialize a console object
540
- from rich.console import Console
541
- from rich.table import Table
542
- from IPython.display import display
543
-
544
- console = Console(record=True)
545
-
546
- # Create a new table
547
- table = Table(show_header=True, header_style="bold magenta", row_styles=["", "dim"])
548
-
549
- # Add columns to the table
550
- table.add_column("Value", style="dim")
551
- table.add_column("Count", style="dim")
552
-
553
- # Add rows to the table
554
- for key, value in data.items():
555
- table.add_row(key, str(value))
556
-
557
- from IPython.display import display
558
-
559
- display_table(console, table, filename)
560
-
561
-
562
- def print_table_with_rich(data, filename=None):
563
- """Print a list of dictionaries as a table using the rich library.
564
-
565
- Example:
566
- >>> data = [{"a": 1, "b": 2, "c": 3}]
567
- >>> print_table_with_rich(data)
568
- ┏━━━┳━━━┳━━━┓
569
- ┃ a ┃ b ┃ c ┃
570
- ┡━━━╇━━━╇━━━┩
571
- │ 1 │ 2 │ 3 │
572
- └───┴───┴───┘
573
- >>> data = [{"a": 1, "b": 2, "c": 3},{"a": 2, "b": 9, "c": 8}]
574
- >>> print_table_with_rich(data)
575
- ┏━━━┳━━━┳━━━┓
576
- ┃ a ┃ b ┃ c ┃
577
- ┡━━━╇━━━╇━━━┩
578
- │ 1 │ 2 │ 3 │
579
- │ 2 │ 9 │ 8 │
580
- └───┴───┴───┘
581
- """
582
- from rich.console import Console
583
- from rich.table import Table
584
-
585
- # Initialize a console object - expects a list of dictionaries
586
- console = Console(record=True)
587
-
588
- # Create a new table
589
- table = Table(show_header=True, header_style="bold magenta", row_styles=["", "dim"])
590
-
591
- # Check if data is empty; if it is, exit
592
- if not data:
593
- console.print("No data provided!")
594
- return
595
-
596
- # Add columns based on keys in the first dictionary
597
- for key in data[0].keys():
598
- table.add_column(key, style="dim")
599
-
600
- # Add rows to the table
601
- for row in data:
602
- table.add_row(*[str(value) for value in row.values()])
603
-
604
- display_table(console, table, filename)
605
-
606
-
607
- if __name__ == "__main__":
608
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3})
609
- import doctest
610
-
611
- doctest.testmod()
612
- # print_list_of_dicts_with_rich([{"a": [1, 2, 3], "b": [4, 5, 6]}])
613
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html")
614
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, show=True)
615
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
616
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
617
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
618
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
619
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
620
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
621
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
622
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
623
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
624
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
625
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
626
- # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
627
- # print_dict_as_html_table({"a": 1, "b": 2, "c
1
+ """A module for displaying data in various formats."""
2
+
3
+ from html import escape
4
+
5
+
6
+ def create_image(console, image_filename):
7
+ """Create an image from the console output."""
8
+ font_size = 15
9
+ from PIL import Image, ImageDraw, ImageFont
10
+
11
+ text = console.export_text() # Get the console output as text.
12
+
13
+ # Create an image from the text
14
+ font_size = 15
15
+ font = ImageFont.load_default() # Use the default font to avoid file path issues.
16
+ # text_width, text_height = ImageDraw.Draw(
17
+ # Image.new("RGB", (100, 100))
18
+ # ).multiline_textsize(text, font=font)
19
+ text_width, text_height = get_multiline_textsize(text, font)
20
+ image = Image.new(
21
+ "RGB", (text_width + 20, text_height + 20), color=(255, 255, 255)
22
+ ) # Add some padding
23
+ d = ImageDraw.Draw(image)
24
+
25
+ # Draw text to image
26
+ d.multiline_text((10, 10), text, font=font, fill=(0, 0, 0))
27
+ # Save the image
28
+ image.save(image_filename)
29
+
30
+
31
+ def display_table(console, table, filename):
32
+ # from rich.console import Console
33
+ # from rich.table import Table
34
+ """Display the table using the rich library and save it to a file if a filename is provided."""
35
+ if filename is not None:
36
+ with open(filename, "w") as f:
37
+ with console.capture() as capture:
38
+ console.print(table)
39
+ f.write(capture.get())
40
+ create_image(console, filename + ".png")
41
+ else:
42
+ console.print(table)
43
+
44
+
45
+ def gen_html_sandwich(html_inner, interactive=False):
46
+ """Wrap the inner HTML content in a header and footer to make a complete HTML document."""
47
+ return html_inner
48
+ if interactive:
49
+ html_header = """
50
+ <html>
51
+ <head>
52
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
53
+ <link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.css" />
54
+ <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.js"></script>
55
+ <style>
56
+ table {
57
+ font-family: Arial, sans-serif;
58
+ border-collapse: collapse;
59
+ width: 100%;
60
+ }
61
+
62
+ td, th {
63
+ border: 1px solid #dddddd;
64
+ text-align: left;
65
+ padding: 8px;
66
+ }
67
+
68
+ tr:nth-child(even) {
69
+ background-color: #dddddd;
70
+ }
71
+ </style>
72
+ <script>
73
+ $(document).ready( function () {
74
+ $('#myTable').DataTable();
75
+ } )
76
+ </script>
77
+ </head>
78
+ <body>
79
+ """
80
+ else:
81
+ html_header = """
82
+ <html>
83
+ <head>
84
+ <style>
85
+ table {
86
+ font-family: Arial, sans-serif;
87
+ border-collapse: collapse;
88
+ width: 100%;
89
+ }
90
+
91
+ td, th {
92
+ border: 1px solid #dddddd;
93
+ text-align: left;
94
+ padding: 8px;
95
+ }
96
+
97
+ tr:nth-child(even) {
98
+ background-color: #dddddd;
99
+ }
100
+ </style>
101
+ </head>
102
+ <body>
103
+ """
104
+
105
+ html_footer = """
106
+ </body>
107
+ </html>
108
+ """
109
+ return html_header + html_inner + html_footer
110
+
111
+
112
+ def view_html(html):
113
+ """Display HTML content in a web browser."""
114
+ import tempfile
115
+ import webbrowser
116
+
117
+ with tempfile.NamedTemporaryFile("w", delete=False, suffix=".html") as f:
118
+ url = "file://" + f.name
119
+ # Write the HTML content to the file
120
+ f.write(html)
121
+
122
+ # Open the URL in the web browser
123
+ webbrowser.open(url)
124
+
125
+
126
+ def human_readable_labeler_creator():
127
+ """Create a function that maps thread ids to human-readable labels.
128
+
129
+ It is structured as a closure, so that the mapping is persistent.
130
+ I.e., when the returned function is called, it will use the same
131
+ dictionary to map thread ids to human-readable labels if it's seen that ID
132
+ before; otherwise, it will add a new entry to the dictionary.
133
+ This will persist across calls to the function.
134
+ """
135
+ d = {}
136
+
137
+ def func(thread_id):
138
+ if thread_id in d:
139
+ return d[thread_id]
140
+ else:
141
+ d[thread_id] = len(d)
142
+ return d[thread_id]
143
+
144
+ return func
145
+
146
+
147
+ def get_multiline_textsize(text, font):
148
+ """Get the size of the text when it is drawn on an image."""
149
+ lines = text.split("\n")
150
+
151
+ # Initialize width and height
152
+ max_width = 0
153
+ total_height = 0
154
+
155
+ for line in lines:
156
+ # Get the size of the text for the line
157
+ box = font.getbbox(line)
158
+ width, height = box[2], box[3]
159
+
160
+ # Update max_width if width of the current line is greater than max_width
161
+ max_width = max(max_width, width)
162
+
163
+ # Add height to total_height
164
+ total_height += height
165
+
166
+ return max_width, total_height
167
+
168
+
169
+ def print_results_long(results, max_rows=None):
170
+ from rich.console import Console
171
+ from rich.table import Table
172
+
173
+ console = Console(record=True)
174
+ table = Table(show_header=True, header_style="bold magenta")
175
+ table.add_column("Result index", style="dim")
176
+ table.add_column("Key", style="dim")
177
+ table.add_column("Value", style="dim")
178
+ list_of_dicts = results.to_dicts()
179
+ num_rows = 0
180
+ for i, results_dict in enumerate(list_of_dicts):
181
+ for key, value in results_dict.items():
182
+ table.add_row(str(i), key, str(value))
183
+ num_rows += 1
184
+ if max_rows is not None and num_rows >= max_rows:
185
+ break
186
+ console.print(table)
187
+
188
+
189
+ def print_dict_with_rich(d, key_name="Key", value_name="Value", filename=None):
190
+ """Print a dictionary as a table using the rich library.
191
+
192
+ Example:
193
+ >>> print_dict_with_rich({"a": 1, "b": 2, "c": 3})
194
+ ┏━━━━━┳━━━━━━━┓
195
+ ┃ Key ┃ Value ┃
196
+ ┡━━━━━╇━━━━━━━┩
197
+ │ a │ 1 │
198
+ │ b │ 2 │
199
+ │ c │ 3 │
200
+ └─────┴───────┘
201
+ """
202
+ from rich.console import Console
203
+ from rich.table import Table
204
+
205
+ console = Console(record=True)
206
+ table = Table(show_header=True, header_style="bold magenta")
207
+ table.add_column(key_name, style="dim")
208
+ table.add_column(value_name, style="dim")
209
+ for key, value in d.items():
210
+ table.add_row(key, str(value))
211
+ console.print(table)
212
+ # display_table(console, table, filename)
213
+
214
+
215
+ def print_dict_as_html_table(
216
+ d,
217
+ show=False,
218
+ key_name="Key",
219
+ value_name="Value",
220
+ filename=None,
221
+ ):
222
+ """Print a dictionary as an HTML table.
223
+
224
+ :param d: The dictionary to print.
225
+ :param show: Whether to display the HTML table in the browser.
226
+ :param key_name: The name of the key column.
227
+ :param value_name: The name of the value column.
228
+ :param filename: The name of the file to save the HTML table to.
229
+ """
230
+ # Start the HTML table
231
+ html_table = f'<table border="1">\n<tr><th>{escape(key_name)}</th><th>{escape(value_name)}</th></tr>\n'
232
+
233
+ # Add rows to the HTML table
234
+ for key, value in d.items():
235
+ html_table += (
236
+ f"<tr><td>{escape(str(key))}</td><td>{escape(str(value))}</td></tr>\n"
237
+ )
238
+
239
+ # Close the HTML table
240
+ html_table += "</table>"
241
+
242
+ # Print the HTML table to console
243
+ # print(html_table)
244
+
245
+ # Write to file if a filename is provided
246
+ if filename:
247
+ with open(filename, "w") as file:
248
+ file.write(html_table)
249
+ else:
250
+ if show:
251
+ view_html(gen_html_sandwich(html_table))
252
+ else:
253
+ return html_table
254
+
255
+
256
+ def print_scenario_list(data):
257
+ from rich.console import Console
258
+ from rich.table import Table
259
+
260
+ new_data = []
261
+ for obs in data:
262
+ try:
263
+ _ = obs.pop("edsl_version")
264
+ _ = obs.pop("edsl_class_name")
265
+ except KeyError as e:
266
+ # print(e)
267
+ pass
268
+ new_data.append(obs)
269
+
270
+ columns = list(new_data[0].keys())
271
+ console = Console(record=True)
272
+
273
+ # Create a table object
274
+ table = Table(show_header=True, header_style="bold magenta", show_lines=True)
275
+ for column in columns:
276
+ table.add_column(column, style="dim")
277
+
278
+ for obs in new_data:
279
+ row = [str(obs[key]) for key in columns]
280
+ table.add_row(*row)
281
+
282
+ console.print(table)
283
+
284
+
285
+ def print_list_of_dicts_with_rich(data, filename=None, split_at_dot=True):
286
+ raise Exception(
287
+ "print_list_of_dicts_with_rich is now called print_dataset_with_rich"
288
+ )
289
+
290
+
291
+ def print_dataset_with_rich(data, filename=None, split_at_dot=True):
292
+ """Initialize console object."""
293
+ """
294
+ The list seems superfluous.
295
+ This prints a list of dictionaries as a table using the rich library.
296
+
297
+ >>> data = [{"a": [1, 2, 3], "b": [4, 5, 6]}]
298
+ >>> print_list_of_dicts_with_rich(data)
299
+ ┏━━━┳━━━┓
300
+ ┃ a ┃ b ┃
301
+ ┡━━━╇━━━┩
302
+ │ 1 │ 4 │
303
+ ├───┼───┤
304
+ │ 2 │ 5 │
305
+ ├───┼───┤
306
+ │ 3 │ 6 │
307
+ └───┴───┘
308
+ """
309
+ from rich.console import Console
310
+ from rich.table import Table
311
+
312
+ console = Console(record=True)
313
+
314
+ # Create a table object
315
+ table = Table(show_header=True, header_style="bold magenta", show_lines=True)
316
+
317
+ # Adding columns to the table
318
+ for d in data:
319
+ for key in d.keys():
320
+ if split_at_dot:
321
+ value = key.replace(".", "\n.")
322
+ else:
323
+ value = key
324
+ table.add_column(value, style="dim")
325
+
326
+ # Adding rows to the table
327
+ num_rows = len(next(iter(data[0].values())))
328
+ for i in range(num_rows):
329
+ row = [str(d[key][i]) for d in data for key in d.keys()]
330
+ table.add_row(*row)
331
+
332
+ console.print(table)
333
+ # display_table(console, table, filename)
334
+
335
+
336
+ def create_latex_table_from_data(data, filename=None, split_at_dot=True):
337
+ """
338
+ This function takes a list of dictionaries and returns a LaTeX table as a string.
339
+ The table can either be printed or written to a file.
340
+
341
+ >>> data = [{"a": [1, 2, 3], "b": [4, 5, 6]}]
342
+ >>> print(create_latex_table_from_data(data))
343
+ \\begin{tabular}{|c|c|}
344
+ \\hline
345
+ a & b \\\\
346
+ \\hline
347
+ 1 & 4 \\\\
348
+ 2 & 5 \\\\
349
+ 3 & 6 \\\\
350
+ \\hline
351
+ \\end{tabular}
352
+ """
353
+
354
+ def escape_latex(s):
355
+ replacements = [
356
+ ("_", r"\_"),
357
+ ("&", r"\&"),
358
+ ("%", r"\%"),
359
+ ("$", r"\$"),
360
+ ("#", r"\#"),
361
+ ("{", r"\{"),
362
+ ("}", r"\}"),
363
+ ("~", r"\textasciitilde{}"),
364
+ ("^", r"\textasciicircum{}"),
365
+ ("\\", r"\textbackslash{}"),
366
+ ]
367
+
368
+ for old, new in replacements:
369
+ s = s.replace(old, new)
370
+ return s
371
+
372
+ # Start the LaTeX table
373
+ latex_table = ["\\begin{tabular}{|" + "c|" * len(data[0]) + "}"]
374
+ latex_table.append("\\hline")
375
+
376
+ # Add the header row
377
+ headers = []
378
+ for key in data[0].keys():
379
+ if split_at_dot:
380
+ value = key.replace(".", "\n.")
381
+ else:
382
+ value = key
383
+ headers.append(escape_latex(value))
384
+ latex_table.append(" & ".join(headers) + " \\\\")
385
+ latex_table.append("\\hline")
386
+
387
+ # Determine the number of rows
388
+ num_rows = len(next(iter(data[0].values())))
389
+
390
+ # Debugging: Print the keys of the dictionaries
391
+ # print("Keys in data[0]:", list(data[0].keys()))
392
+
393
+ # Add the data rows
394
+ for i in range(num_rows):
395
+ row = []
396
+ for key in data[0].keys():
397
+ for d in data:
398
+ try:
399
+ row.append(escape_latex(str(d[key][i])))
400
+ except KeyError as e:
401
+ print(
402
+ f"KeyError: {e} - Key '{key}' not found in data dictionary. The keys are {list(d.keys())}"
403
+ )
404
+ raise
405
+ latex_table.append(" & ".join(row) + " \\\\")
406
+
407
+ latex_table.append("\\hline")
408
+ latex_table.append("\\end{tabular}")
409
+
410
+ # Join all parts into a single string
411
+ latex_table_str = "\n".join(latex_table)
412
+
413
+ # Write to file if filename is provided
414
+ if filename:
415
+ with open(filename, "w") as f:
416
+ f.write(latex_table_str)
417
+ print(f"Table written to {filename}")
418
+
419
+ return latex_table_str
420
+
421
+
422
+ def print_list_of_dicts_as_html_table(data, interactive=True):
423
+ """Print a list of dictionaries as an HTML table.
424
+
425
+ :param data: The list of dictionaries to print.
426
+ :param filename: The name of the file to save the HTML table to.
427
+ :param interactive: Whether to make the table interactive using DataTables.
428
+ """
429
+ style = """
430
+ <style>
431
+ table {
432
+ width: 100%;
433
+ border-collapse: collapse;
434
+ }
435
+ table, th, td {
436
+ border: 1px solid black;
437
+ }
438
+ th, td {
439
+ padding: 10px;
440
+ text-align: left;
441
+ }
442
+ </style>
443
+ """
444
+ html_table = style + '<table id="myTable" class="display">\n'
445
+ html_table += " <thead>\n"
446
+ # Add the header row
447
+ headers = [key for d in data for key in d.keys()]
448
+ html_table += " <tr>\n"
449
+ for header in headers:
450
+ html_table += f" <th>{header}</th>\n"
451
+ html_table += " </tr>\n"
452
+ html_table += " </thead>\n</tbody>\n"
453
+
454
+ # Determine the number of rows
455
+ num_rows = max(len(values) for d in data for values in d.values())
456
+
457
+ # Add the data rows
458
+ for i in range(num_rows):
459
+ html_table += " <tr>\n"
460
+ for d in data:
461
+ for key in d.keys():
462
+ value = d[key][i] if i < len(d[key]) else ""
463
+ html_table += f" <td>{value}</td>\n"
464
+ html_table += " </tr>\n"
465
+
466
+ # Close the table
467
+ html_table += "</tbody>\n"
468
+ html_table += "</table>"
469
+ return gen_html_sandwich(html_table, interactive=interactive)
470
+
471
+
472
+ def print_list_of_dicts_as_markdown_table(data, filename=None):
473
+ """Print a list of dictionaries as a Markdown table.
474
+
475
+ :param data: The list of dictionaries to print.
476
+ :param filename: The name of the file to save the Markdown table to.
477
+ """
478
+ if not data:
479
+ print("No data provided")
480
+ return
481
+
482
+ # Gather all unique headers
483
+ # headers = list({key for d in data for key in d.keys()})
484
+ headers = []
485
+ for column in data:
486
+ headers.append(list(column.keys())[0])
487
+
488
+ markdown_table = "| " + " | ".join(headers) + " |\n"
489
+ markdown_table += "|-" + "-|-".join(["" for _ in headers]) + "-|\n"
490
+
491
+ num_rows = len(next(iter(data[0].values())))
492
+ for i in range(num_rows):
493
+ row = [str(d[key][i]) for d in data for key in d.keys()]
494
+ # table.add_row(*row)
495
+ markdown_table += "| " + " | ".join(row) + " |\n"
496
+
497
+ # Output or save to file
498
+ if filename:
499
+ with open(filename, "w") as f:
500
+ f.write(markdown_table)
501
+ else:
502
+ print(markdown_table)
503
+
504
+
505
+ def print_public_methods_with_doc(obj):
506
+ """Print the public methods of an object along with their docstrings."""
507
+ from rich.console import Console
508
+ from rich.table import Table
509
+
510
+ console = Console()
511
+ public_methods_with_docstrings = [
512
+ (method, getattr(obj, method).__doc__)
513
+ for method in dir(obj)
514
+ if callable(getattr(obj, method))
515
+ and not method.startswith("_")
516
+ and method != "methods"
517
+ ]
518
+
519
+ for method, doc in public_methods_with_docstrings:
520
+ if doc:
521
+ console.print(f"[bold]{method}:[/bold]", style="green")
522
+ console.print(f"\t{doc.strip()}", style="yellow")
523
+
524
+
525
+ def print_tally_with_rich(data, filename=None):
526
+ """Print a tally of values in a list using the rich library.
527
+
528
+ Example:
529
+ >>> data = {'a':12, 'b':14, 'c':9}
530
+ >>> print_tally_with_rich(data)
531
+ ┏━━━━━━━┳━━━━━━━┓
532
+ ┃ Value ┃ Count ┃
533
+ ┡━━━━━━━╇━━━━━━━┩
534
+ │ a │ 12 │
535
+ │ b │ 14 │
536
+ │ c │ 9 │
537
+ └───────┴───────┘
538
+ """
539
+ # Initialize a console object
540
+ from rich.console import Console
541
+ from rich.table import Table
542
+ from IPython.display import display
543
+
544
+ console = Console(record=True)
545
+
546
+ # Create a new table
547
+ table = Table(show_header=True, header_style="bold magenta", row_styles=["", "dim"])
548
+
549
+ # Add columns to the table
550
+ table.add_column("Value", style="dim")
551
+ table.add_column("Count", style="dim")
552
+
553
+ # Add rows to the table
554
+ for key, value in data.items():
555
+ table.add_row(key, str(value))
556
+
557
+ from IPython.display import display
558
+
559
+ display_table(console, table, filename)
560
+
561
+
562
+ def print_table_with_rich(data, filename=None):
563
+ """Print a list of dictionaries as a table using the rich library.
564
+
565
+ Example:
566
+ >>> data = [{"a": 1, "b": 2, "c": 3}]
567
+ >>> print_table_with_rich(data)
568
+ ┏━━━┳━━━┳━━━┓
569
+ ┃ a ┃ b ┃ c ┃
570
+ ┡━━━╇━━━╇━━━┩
571
+ │ 1 │ 2 │ 3 │
572
+ └───┴───┴───┘
573
+ >>> data = [{"a": 1, "b": 2, "c": 3},{"a": 2, "b": 9, "c": 8}]
574
+ >>> print_table_with_rich(data)
575
+ ┏━━━┳━━━┳━━━┓
576
+ ┃ a ┃ b ┃ c ┃
577
+ ┡━━━╇━━━╇━━━┩
578
+ │ 1 │ 2 │ 3 │
579
+ │ 2 │ 9 │ 8 │
580
+ └───┴───┴───┘
581
+ """
582
+ from rich.console import Console
583
+ from rich.table import Table
584
+
585
+ # Initialize a console object - expects a list of dictionaries
586
+ console = Console(record=True)
587
+
588
+ # Create a new table
589
+ table = Table(show_header=True, header_style="bold magenta", row_styles=["", "dim"])
590
+
591
+ # Check if data is empty; if it is, exit
592
+ if not data:
593
+ console.print("No data provided!")
594
+ return
595
+
596
+ # Add columns based on keys in the first dictionary
597
+ for key in data[0].keys():
598
+ table.add_column(key, style="dim")
599
+
600
+ # Add rows to the table
601
+ for row in data:
602
+ table.add_row(*[str(value) for value in row.values()])
603
+
604
+ display_table(console, table, filename)
605
+
606
+
607
+ if __name__ == "__main__":
608
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3})
609
+ import doctest
610
+
611
+ doctest.testmod()
612
+ # print_list_of_dicts_with_rich([{"a": [1, 2, 3], "b": [4, 5, 6]}])
613
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html")
614
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, show=True)
615
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
616
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
617
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
618
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
619
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
620
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
621
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
622
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
623
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
624
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
625
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=True)
626
+ # print_dict_as_html_table({"a": 1, "b": 2, "c": 3}, filename="test.html", show=False)
627
+ # print_dict_as_html_table({"a": 1, "b": 2, "c