edsl 0.1.39__py3-none-any.whl → 0.1.39.dev1__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 (212) hide show
  1. edsl/Base.py +116 -197
  2. edsl/__init__.py +7 -15
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +147 -351
  5. edsl/agents/AgentList.py +73 -211
  6. edsl/agents/Invigilator.py +50 -101
  7. edsl/agents/InvigilatorBase.py +70 -62
  8. edsl/agents/PromptConstructor.py +225 -143
  9. edsl/agents/__init__.py +1 -0
  10. edsl/agents/prompt_helpers.py +3 -3
  11. edsl/auto/AutoStudy.py +5 -18
  12. edsl/auto/StageBase.py +40 -53
  13. edsl/auto/StageQuestions.py +1 -2
  14. edsl/auto/utilities.py +6 -0
  15. edsl/config.py +2 -22
  16. edsl/conversation/car_buying.py +1 -2
  17. edsl/coop/PriceFetcher.py +1 -1
  18. edsl/coop/coop.py +47 -125
  19. edsl/coop/utils.py +14 -14
  20. edsl/data/Cache.py +27 -45
  21. edsl/data/CacheEntry.py +15 -12
  22. edsl/data/CacheHandler.py +12 -31
  23. edsl/data/RemoteCacheSync.py +46 -154
  24. edsl/data/__init__.py +3 -4
  25. edsl/data_transfer_models.py +1 -2
  26. edsl/enums.py +0 -27
  27. edsl/exceptions/__init__.py +50 -50
  28. edsl/exceptions/agents.py +0 -12
  29. edsl/exceptions/questions.py +6 -24
  30. edsl/exceptions/scenarios.py +0 -7
  31. edsl/inference_services/AnthropicService.py +19 -38
  32. edsl/inference_services/AwsBedrock.py +2 -0
  33. edsl/inference_services/AzureAI.py +2 -0
  34. edsl/inference_services/GoogleService.py +12 -7
  35. edsl/inference_services/InferenceServiceABC.py +85 -18
  36. edsl/inference_services/InferenceServicesCollection.py +79 -120
  37. edsl/inference_services/MistralAIService.py +3 -0
  38. edsl/inference_services/OpenAIService.py +35 -47
  39. edsl/inference_services/PerplexityService.py +3 -0
  40. edsl/inference_services/TestService.py +10 -11
  41. edsl/inference_services/TogetherAIService.py +3 -5
  42. edsl/jobs/Answers.py +14 -1
  43. edsl/jobs/Jobs.py +431 -356
  44. edsl/jobs/JobsChecks.py +10 -35
  45. edsl/jobs/JobsPrompts.py +4 -6
  46. edsl/jobs/JobsRemoteInferenceHandler.py +133 -205
  47. edsl/jobs/buckets/BucketCollection.py +3 -44
  48. edsl/jobs/buckets/TokenBucket.py +21 -53
  49. edsl/jobs/interviews/Interview.py +408 -143
  50. edsl/jobs/runners/JobsRunnerAsyncio.py +403 -88
  51. edsl/jobs/runners/JobsRunnerStatus.py +165 -133
  52. edsl/jobs/tasks/QuestionTaskCreator.py +19 -21
  53. edsl/jobs/tasks/TaskHistory.py +18 -38
  54. edsl/jobs/tasks/task_status_enum.py +2 -0
  55. edsl/language_models/KeyLookup.py +30 -0
  56. edsl/language_models/LanguageModel.py +236 -194
  57. edsl/language_models/ModelList.py +19 -28
  58. edsl/language_models/__init__.py +2 -1
  59. edsl/language_models/registry.py +190 -0
  60. edsl/language_models/repair.py +2 -2
  61. edsl/language_models/unused/ReplicateBase.py +83 -0
  62. edsl/language_models/utilities.py +4 -5
  63. edsl/notebooks/Notebook.py +14 -19
  64. edsl/prompts/Prompt.py +39 -29
  65. edsl/questions/{answer_validator_mixin.py → AnswerValidatorMixin.py} +2 -47
  66. edsl/questions/QuestionBase.py +214 -68
  67. edsl/questions/{question_base_gen_mixin.py → QuestionBaseGenMixin.py} +50 -57
  68. edsl/questions/QuestionBasePromptsMixin.py +3 -7
  69. edsl/questions/QuestionBudget.py +1 -1
  70. edsl/questions/QuestionCheckBox.py +3 -3
  71. edsl/questions/QuestionExtract.py +7 -5
  72. edsl/questions/QuestionFreeText.py +3 -2
  73. edsl/questions/QuestionList.py +18 -10
  74. edsl/questions/QuestionMultipleChoice.py +23 -67
  75. edsl/questions/QuestionNumerical.py +4 -2
  76. edsl/questions/QuestionRank.py +17 -7
  77. edsl/questions/{response_validator_abc.py → ResponseValidatorABC.py} +26 -40
  78. edsl/questions/SimpleAskMixin.py +3 -4
  79. edsl/questions/__init__.py +1 -2
  80. edsl/questions/derived/QuestionLinearScale.py +3 -6
  81. edsl/questions/derived/QuestionTopK.py +1 -1
  82. edsl/questions/descriptors.py +3 -17
  83. edsl/questions/question_registry.py +1 -1
  84. edsl/results/CSSParameterizer.py +1 -1
  85. edsl/results/Dataset.py +7 -170
  86. edsl/results/DatasetExportMixin.py +305 -168
  87. edsl/results/DatasetTree.py +8 -28
  88. edsl/results/Result.py +206 -298
  89. edsl/results/Results.py +131 -149
  90. edsl/results/ResultsDBMixin.py +238 -0
  91. edsl/results/ResultsExportMixin.py +0 -2
  92. edsl/results/{results_selector.py → Selector.py} +13 -23
  93. edsl/results/TableDisplay.py +171 -98
  94. edsl/results/__init__.py +1 -1
  95. edsl/scenarios/FileStore.py +239 -150
  96. edsl/scenarios/Scenario.py +193 -90
  97. edsl/scenarios/ScenarioHtmlMixin.py +3 -4
  98. edsl/scenarios/{scenario_join.py → ScenarioJoin.py} +6 -10
  99. edsl/scenarios/ScenarioList.py +244 -415
  100. edsl/scenarios/ScenarioListExportMixin.py +7 -0
  101. edsl/scenarios/ScenarioListPdfMixin.py +37 -15
  102. edsl/scenarios/__init__.py +2 -1
  103. edsl/study/ObjectEntry.py +1 -1
  104. edsl/study/SnapShot.py +1 -1
  105. edsl/study/Study.py +12 -5
  106. edsl/surveys/Rule.py +4 -5
  107. edsl/surveys/RuleCollection.py +27 -25
  108. edsl/surveys/Survey.py +791 -270
  109. edsl/surveys/SurveyCSS.py +8 -20
  110. edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +9 -11
  111. edsl/surveys/__init__.py +2 -4
  112. edsl/surveys/descriptors.py +2 -6
  113. edsl/surveys/instructions/ChangeInstruction.py +2 -1
  114. edsl/surveys/instructions/Instruction.py +13 -4
  115. edsl/surveys/instructions/InstructionCollection.py +6 -11
  116. edsl/templates/error_reporting/interview_details.html +1 -1
  117. edsl/templates/error_reporting/report.html +1 -1
  118. edsl/tools/plotting.py +1 -1
  119. edsl/utilities/utilities.py +23 -35
  120. {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/METADATA +10 -12
  121. edsl-0.1.39.dev1.dist-info/RECORD +277 -0
  122. {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/WHEEL +1 -1
  123. edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
  124. edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
  125. edsl/agents/question_option_processor.py +0 -172
  126. edsl/coop/CoopFunctionsMixin.py +0 -15
  127. edsl/coop/ExpectedParrotKeyHandler.py +0 -125
  128. edsl/exceptions/inference_services.py +0 -5
  129. edsl/inference_services/AvailableModelCacheHandler.py +0 -184
  130. edsl/inference_services/AvailableModelFetcher.py +0 -215
  131. edsl/inference_services/ServiceAvailability.py +0 -135
  132. edsl/inference_services/data_structures.py +0 -134
  133. edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -223
  134. edsl/jobs/FetchInvigilator.py +0 -47
  135. edsl/jobs/InterviewTaskManager.py +0 -98
  136. edsl/jobs/InterviewsConstructor.py +0 -50
  137. edsl/jobs/JobsComponentConstructor.py +0 -189
  138. edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
  139. edsl/jobs/RequestTokenEstimator.py +0 -30
  140. edsl/jobs/async_interview_runner.py +0 -138
  141. edsl/jobs/buckets/TokenBucketAPI.py +0 -211
  142. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  143. edsl/jobs/check_survey_scenario_compatibility.py +0 -85
  144. edsl/jobs/data_structures.py +0 -120
  145. edsl/jobs/decorators.py +0 -35
  146. edsl/jobs/jobs_status_enums.py +0 -9
  147. edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
  148. edsl/jobs/results_exceptions_handler.py +0 -98
  149. edsl/language_models/ComputeCost.py +0 -63
  150. edsl/language_models/PriceManager.py +0 -127
  151. edsl/language_models/RawResponseHandler.py +0 -106
  152. edsl/language_models/ServiceDataSources.py +0 -0
  153. edsl/language_models/key_management/KeyLookup.py +0 -63
  154. edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
  155. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  156. edsl/language_models/key_management/__init__.py +0 -0
  157. edsl/language_models/key_management/models.py +0 -131
  158. edsl/language_models/model.py +0 -256
  159. edsl/notebooks/NotebookToLaTeX.py +0 -142
  160. edsl/questions/ExceptionExplainer.py +0 -77
  161. edsl/questions/HTMLQuestion.py +0 -103
  162. edsl/questions/QuestionMatrix.py +0 -265
  163. edsl/questions/data_structures.py +0 -20
  164. edsl/questions/loop_processor.py +0 -149
  165. edsl/questions/response_validator_factory.py +0 -34
  166. edsl/questions/templates/matrix/__init__.py +0 -1
  167. edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
  168. edsl/questions/templates/matrix/question_presentation.jinja +0 -20
  169. edsl/results/MarkdownToDocx.py +0 -122
  170. edsl/results/MarkdownToPDF.py +0 -111
  171. edsl/results/TextEditor.py +0 -50
  172. edsl/results/file_exports.py +0 -252
  173. edsl/results/smart_objects.py +0 -96
  174. edsl/results/table_data_class.py +0 -12
  175. edsl/results/table_renderers.py +0 -118
  176. edsl/scenarios/ConstructDownloadLink.py +0 -109
  177. edsl/scenarios/DocumentChunker.py +0 -102
  178. edsl/scenarios/DocxScenario.py +0 -16
  179. edsl/scenarios/PdfExtractor.py +0 -40
  180. edsl/scenarios/directory_scanner.py +0 -96
  181. edsl/scenarios/file_methods.py +0 -85
  182. edsl/scenarios/handlers/__init__.py +0 -13
  183. edsl/scenarios/handlers/csv.py +0 -49
  184. edsl/scenarios/handlers/docx.py +0 -76
  185. edsl/scenarios/handlers/html.py +0 -37
  186. edsl/scenarios/handlers/json.py +0 -111
  187. edsl/scenarios/handlers/latex.py +0 -5
  188. edsl/scenarios/handlers/md.py +0 -51
  189. edsl/scenarios/handlers/pdf.py +0 -68
  190. edsl/scenarios/handlers/png.py +0 -39
  191. edsl/scenarios/handlers/pptx.py +0 -105
  192. edsl/scenarios/handlers/py.py +0 -294
  193. edsl/scenarios/handlers/sql.py +0 -313
  194. edsl/scenarios/handlers/sqlite.py +0 -149
  195. edsl/scenarios/handlers/txt.py +0 -33
  196. edsl/scenarios/scenario_selector.py +0 -156
  197. edsl/surveys/ConstructDAG.py +0 -92
  198. edsl/surveys/EditSurvey.py +0 -221
  199. edsl/surveys/InstructionHandler.py +0 -100
  200. edsl/surveys/MemoryManagement.py +0 -72
  201. edsl/surveys/RuleManager.py +0 -172
  202. edsl/surveys/Simulator.py +0 -75
  203. edsl/surveys/SurveyToApp.py +0 -141
  204. edsl/utilities/PrettyList.py +0 -56
  205. edsl/utilities/is_notebook.py +0 -18
  206. edsl/utilities/is_valid_variable_name.py +0 -11
  207. edsl/utilities/remove_edsl_version.py +0 -24
  208. edsl-0.1.39.dist-info/RECORD +0 -358
  209. /edsl/questions/{register_questions_meta.py → RegisterQuestionsMeta.py} +0 -0
  210. /edsl/results/{results_fetch_mixin.py → ResultsFetchMixin.py} +0 -0
  211. /edsl/results/{results_tools_mixin.py → ResultsToolsMixin.py} +0 -0
  212. {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/LICENSE +0 -0
@@ -1,11 +1,12 @@
1
1
  """Mixin class for exporting results."""
2
2
 
3
+ import base64
4
+ import csv
3
5
  import io
4
- import warnings
5
- import textwrap
6
- from typing import Optional, Tuple, Union, List
6
+ import html
7
+ from typing import Optional
7
8
 
8
- from edsl.results.file_exports import CSVExport, ExcelExport, JSONLExport, SQLiteExport
9
+ from typing import Literal, Optional, Union, List
9
10
 
10
11
 
11
12
  class DatasetExportMixin:
@@ -36,7 +37,7 @@ class DatasetExportMixin:
36
37
 
37
38
  >>> from edsl.results import Results
38
39
  >>> sorted(Results.example().select().relevant_columns(data_type = "model"))
39
- ['model.frequency_penalty', ...]
40
+ ['model.frequency_penalty', 'model.logprobs', 'model.max_tokens', 'model.model', 'model.presence_penalty', 'model.temperature', 'model.top_logprobs', 'model.top_p']
40
41
 
41
42
  >>> Results.example().relevant_columns(data_type = "flimflam")
42
43
  Traceback (most recent call last):
@@ -71,7 +72,7 @@ class DatasetExportMixin:
71
72
  def num_observations(self):
72
73
  """Return the number of observations in the dataset.
73
74
 
74
- >>> from edsl.results.Results import Results
75
+ >>> from edsl.results import Results
75
76
  >>> Results.example().num_observations()
76
77
  4
77
78
  """
@@ -143,148 +144,299 @@ class DatasetExportMixin:
143
144
  for value in list_of_values:
144
145
  print(f"{key}: {value}")
145
146
 
146
- def _get_tabular_data(
147
- self,
148
- remove_prefix: bool = False,
149
- pretty_labels: Optional[dict] = None,
150
- ) -> Tuple[List[str], List[List]]:
151
- """Internal method to get tabular data in a standard format.
152
-
153
- Args:
154
- remove_prefix: Whether to remove the prefix from column names
155
- pretty_labels: Dictionary mapping original column names to pretty labels
156
-
157
- Returns:
158
- Tuple containing (header_row, data_rows)
159
- """
160
- if pretty_labels is None:
161
- pretty_labels = {}
162
-
163
- return self._make_tabular(
164
- remove_prefix=remove_prefix, pretty_labels=pretty_labels
165
- )
166
-
167
- def to_jsonl(self, filename: Optional[str] = None) -> Optional["FileStore"]:
168
- """Export the results to a FileStore instance containing JSONL data."""
169
- exporter = JSONLExport(data=self, filename=filename)
170
- return exporter.export()
171
-
172
- def to_sqlite(
173
- self,
174
- filename: Optional[str] = None,
175
- remove_prefix: bool = False,
176
- pretty_labels: Optional[dict] = None,
177
- table_name: str = "results",
178
- if_exists: str = "replace",
179
- ) -> Optional["FileStore"]:
180
- """Export the results to a SQLite database file."""
181
- exporter = SQLiteExport(
182
- data=self,
183
- filename=filename,
184
- remove_prefix=remove_prefix,
185
- pretty_labels=pretty_labels,
186
- table_name=table_name,
187
- if_exists=if_exists,
188
- )
189
- return exporter.export()
147
+ # def print(
148
+ # self,
149
+ # pretty_labels: Optional[dict] = None,
150
+ # filename: Optional[str] = None,
151
+ # format: Optional[Literal["rich", "html", "markdown", "latex"]] = None,
152
+ # interactive: bool = False,
153
+ # split_at_dot: bool = True,
154
+ # max_rows=None,
155
+ # tee=False,
156
+ # iframe=False,
157
+ # iframe_height: int = 200,
158
+ # iframe_width: int = 600,
159
+ # web=False,
160
+ # return_string: bool = False,
161
+ # ) -> Union[None, str, "Results"]:
162
+ # """Print the results in a pretty format.
163
+
164
+ # :param pretty_labels: A dictionary of pretty labels for the columns.
165
+ # :param filename: The filename to save the results to.
166
+ # :param format: The format to print the results in. Options are 'rich', 'html', 'markdown', or 'latex'.
167
+ # :param interactive: Whether to print the results interactively in a Jupyter notebook.
168
+ # :param split_at_dot: Whether to split the column names at the last dot w/ a newline.
169
+ # :param max_rows: The maximum number of rows to print.
170
+ # :param tee: Whether to return the dataset.
171
+ # :param iframe: Whether to display the table in an iframe.
172
+ # :param iframe_height: The height of the iframe.
173
+ # :param iframe_width: The width of the iframe.
174
+ # :param web: Whether to display the table in a web browser.
175
+ # :param return_string: Whether to return the output as a string instead of printing.
176
+
177
+ # :return: None if tee is False and return_string is False, the dataset if tee is True, or a string if return_string is True.
178
+
179
+ # Example: Print in rich format at the terminal
180
+
181
+ # >>> from edsl.results import Results
182
+ # >>> r = Results.example()
183
+ # >>> r.select('how_feeling').print(format = "rich")
184
+ # ┏━━━━━━━━━━━━━━┓
185
+ # ┃ answer ┃
186
+ # ┃ .how_feeling ┃
187
+ # ┡━━━━━━━━━━━━━━┩
188
+ # │ OK │
189
+ # ├──────────────┤
190
+ # │ Great
191
+ # ├──────────────┤
192
+ # │ Terrible │
193
+ # ├──────────────┤
194
+ # │ OK │
195
+ # └──────────────┘
196
+
197
+ # >>> r = Results.example()
198
+ # >>> r2 = r.select("how_feeling").print(format = "rich", tee = True, max_rows = 2)
199
+ # ┏━━━━━━━━━━━━━━┓
200
+ # ┃ answer ┃
201
+ # ┃ .how_feeling ┃
202
+ # ┡━━━━━━━━━━━━━━┩
203
+ # │ OK │
204
+ # ├──────────────┤
205
+ # │ Great │
206
+ # └──────────────┘
207
+ # >>> r2
208
+ # Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
209
+
210
+ # >>> r.select('how_feeling').print(format = "rich", max_rows = 2)
211
+ # ┏━━━━━━━━━━━━━━┓
212
+ # ┃ answer ┃
213
+ # ┃ .how_feeling ┃
214
+ # ┡━━━━━━━━━━━━━━┩
215
+ # │ OK │
216
+ # ├──────────────┤
217
+ # │ Great │
218
+ # └──────────────┘
219
+
220
+ # >>> r.select('how_feeling').print(format = "rich", split_at_dot = False)
221
+ # ┏━━━━━━━━━━━━━━━━━━━━┓
222
+ # ┃ answer.how_feeling ┃
223
+ # ┡━━━━━━━━━━━━━━━━━━━━┩
224
+ # │ OK │
225
+ # ├────────────────────┤
226
+ # │ Great │
227
+ # ├────────────────────┤
228
+ # │ Terrible │
229
+ # ├────────────────────┤
230
+ # │ OK │
231
+ # └────────────────────┘
232
+
233
+ # Example: using the pretty_labels parameter
234
+
235
+ # >>> r.select('how_feeling').print(format="rich", pretty_labels = {'answer.how_feeling': "How are you feeling"})
236
+ # ┏━━━━━━━━━━━━━━━━━━━━━┓
237
+ # ┃ How are you feeling ┃
238
+ # ┡━━━━━━━━━━━━━━━━━━━━━┩
239
+ # │ OK │
240
+ # ├─────────────────────┤
241
+ # │ Great │
242
+ # ├─────────────────────┤
243
+ # │ Terrible │
244
+ # ├─────────────────────┤
245
+ # │ OK │
246
+ # └─────────────────────┘
247
+
248
+ # Example: printing in markdown format
249
+
250
+ # >>> r.select('how_feeling').print(format='markdown')
251
+ # | answer.how_feeling |
252
+ # |--|
253
+ # | OK |
254
+ # | Great |
255
+ # | Terrible |
256
+ # | OK |
257
+ # ...
258
+
259
+ # >>> r.select('how_feeling').print(format='latex')
260
+ # \\begin{tabular}{l}
261
+ # ...
262
+ # \\end{tabular}
263
+ # <BLANKLINE>
264
+ # """
265
+ # from IPython.display import HTML, display
266
+ # from edsl.utilities.utilities import is_notebook
267
+ # import io
268
+ # import sys
269
+
270
+ # def _determine_format(format):
271
+ # if format is None:
272
+ # if is_notebook():
273
+ # format = "html"
274
+ # else:
275
+ # format = "rich"
276
+ # if format not in ["rich", "html", "markdown", "latex"]:
277
+ # raise ValueError(
278
+ # "format must be one of 'rich', 'html', 'markdown', or 'latex'."
279
+ # )
280
+
281
+ # return format
282
+
283
+ # format = _determine_format(format)
284
+
285
+ # if pretty_labels is None:
286
+ # pretty_labels = {}
287
+
288
+ # if pretty_labels != {}: # only split at dot if there are no pretty labels
289
+ # split_at_dot = False
290
+
291
+ # def _create_data():
292
+ # for index, entry in enumerate(self):
293
+ # key, list_of_values = list(entry.items())[0]
294
+ # yield {pretty_labels.get(key, key): list_of_values[:max_rows]}
295
+
296
+ # new_data = list(_create_data())
297
+
298
+ # # Capture output if return_string is True
299
+ # if return_string:
300
+ # old_stdout = sys.stdout
301
+ # sys.stdout = io.StringIO()
302
+
303
+ # output = None
304
+
305
+ # if format == "rich":
306
+ # from edsl.utilities.interface import print_dataset_with_rich
307
+
308
+ # output = print_dataset_with_rich(
309
+ # new_data, filename=filename, split_at_dot=split_at_dot
310
+ # )
311
+ # elif format == "markdown":
312
+ # from edsl.utilities.interface import print_list_of_dicts_as_markdown_table
313
+
314
+ # output = print_list_of_dicts_as_markdown_table(new_data, filename=filename)
315
+ # elif format == "latex":
316
+ # df = self.to_pandas()
317
+ # df.columns = [col.replace("_", " ") for col in df.columns]
318
+ # latex_string = df.to_latex(index=False)
319
+
320
+ # if filename is not None:
321
+ # with open(filename, "w") as f:
322
+ # f.write(latex_string)
323
+ # else:
324
+ # print(latex_string)
325
+ # output = latex_string
326
+ # elif format == "html":
327
+ # from edsl.utilities.interface import print_list_of_dicts_as_html_table
328
+
329
+ # html_source = print_list_of_dicts_as_html_table(
330
+ # new_data, interactive=interactive
331
+ # )
332
+
333
+ # if iframe:
334
+ # iframe = f""""
335
+ # <iframe srcdoc="{ html.escape(html_source) }" style="width: {iframe_width}px; height: {iframe_height}px;"></iframe>
336
+ # """
337
+ # display(HTML(iframe))
338
+ # elif is_notebook():
339
+ # display(HTML(html_source))
340
+ # else:
341
+ # from edsl.utilities.interface import view_html
342
+
343
+ # view_html(html_source)
344
+
345
+ # output = html_source
346
+
347
+ # # Restore stdout and get captured output if return_string is True
348
+ # if return_string:
349
+ # captured_output = sys.stdout.getvalue()
350
+ # sys.stdout = old_stdout
351
+ # return captured_output or output
352
+
353
+ # if tee:
354
+ # return self
355
+
356
+ # return None
190
357
 
191
358
  def to_csv(
192
359
  self,
193
360
  filename: Optional[str] = None,
194
361
  remove_prefix: bool = False,
362
+ download_link: bool = False,
195
363
  pretty_labels: Optional[dict] = None,
196
- ) -> Optional["FileStore"]:
197
- """Export the results to a FileStore instance containing CSV data."""
198
- exporter = CSVExport(
199
- data=self,
200
- filename=filename,
201
- remove_prefix=remove_prefix,
202
- pretty_labels=pretty_labels,
203
- )
204
- return exporter.export()
364
+ ):
365
+ """Export the results to a CSV file.
205
366
 
206
- def to_excel(
207
- self,
208
- filename: Optional[str] = None,
209
- remove_prefix: bool = False,
210
- pretty_labels: Optional[dict] = None,
211
- sheet_name: Optional[str] = None,
212
- ) -> Optional["FileStore"]:
213
- """Export the results to a FileStore instance containing Excel data."""
214
- exporter = ExcelExport(
215
- data=self,
216
- filename=filename,
217
- remove_prefix=remove_prefix,
218
- pretty_labels=pretty_labels,
219
- sheet_name=sheet_name,
220
- )
221
- return exporter.export()
367
+ :param filename: The filename to save the CSV file to.
368
+ :param remove_prefix: Whether to remove the prefix from the column names.
369
+ :param download_link: Whether to display a download link in a Jupyter notebook.
222
370
 
223
- def _db(self, remove_prefix: bool = True):
224
- """Create a SQLite database in memory and return the connection.
371
+ Example:
225
372
 
226
- Args:
227
- shape: The shape of the data in the database (wide or long)
228
- remove_prefix: Whether to remove the prefix from the column names
373
+ >>> from edsl.results import Results
374
+ >>> r = Results.example()
375
+ >>> r.select('how_feeling').to_csv()
376
+ 'answer.how_feeling\\r\\nOK\\r\\nGreat\\r\\nTerrible\\r\\nOK\\r\\n'
377
+
378
+ >>> r.select('how_feeling').to_csv(pretty_labels = {'answer.how_feeling': "How are you feeling"})
379
+ 'How are you feeling\\r\\nOK\\r\\nGreat\\r\\nTerrible\\r\\nOK\\r\\n'
380
+
381
+ >>> import tempfile
382
+ >>> filename = tempfile.NamedTemporaryFile(delete=False).name
383
+ >>> r.select('how_feeling').to_csv(filename = filename)
384
+ >>> import os
385
+ >>> import csv
386
+ >>> with open(filename, newline='') as f:
387
+ ... reader = csv.reader(f)
388
+ ... for row in reader:
389
+ ... print(row)
390
+ ['answer.how_feeling']
391
+ ['OK']
392
+ ['Great']
393
+ ['Terrible']
394
+ ['OK']
229
395
 
230
- Returns:
231
- A database connection
232
396
  """
233
- from sqlalchemy import create_engine
234
-
235
- engine = create_engine("sqlite:///:memory:")
236
- if remove_prefix:
237
- df = self.remove_prefix().to_pandas(lists_as_strings=True)
238
- else:
239
- df = self.to_pandas(lists_as_strings=True)
240
- df.to_sql(
241
- "self",
242
- engine,
243
- index=False,
244
- if_exists="replace",
397
+ if pretty_labels is None:
398
+ pretty_labels = {}
399
+ header, rows = self._make_tabular(
400
+ remove_prefix=remove_prefix, pretty_labels=pretty_labels
245
401
  )
246
- return engine.connect()
247
402
 
248
- def sql(
249
- self,
250
- query: str,
251
- transpose: bool = None,
252
- transpose_by: str = None,
253
- remove_prefix: bool = True,
254
- ) -> Union["pd.DataFrame", str]:
255
- """Execute a SQL query and return the results as a DataFrame.
256
-
257
- Args:
258
- query: The SQL query to execute
259
- shape: The shape of the data in the database (wide or long)
260
- remove_prefix: Whether to remove the prefix from the column names
261
- transpose: Whether to transpose the DataFrame
262
- transpose_by: The column to use as the index when transposing
263
- csv: Whether to return the DataFrame as a CSV string
264
- to_list: Whether to return the results as a list
265
- to_latex: Whether to return the results as LaTeX
266
- filename: Optional filename to save the results to
267
-
268
- Returns:
269
- DataFrame, CSV string, list, or LaTeX string depending on parameters
403
+ if filename is not None:
404
+ with open(filename, "w") as f:
405
+ writer = csv.writer(f)
406
+ writer.writerow(header)
407
+ writer.writerows(rows)
408
+ # print(f"Saved to {filename}")
409
+ else:
410
+ output = io.StringIO()
411
+ writer = csv.writer(output)
412
+ writer.writerow(header)
413
+ writer.writerows(rows)
414
+
415
+ if download_link:
416
+ from IPython.display import HTML, display
417
+
418
+ csv_file = output.getvalue()
419
+ b64 = base64.b64encode(csv_file.encode()).decode()
420
+ download_link = f'<a href="data:file/csv;base64,{b64}" download="my_data.csv">Download CSV file</a>'
421
+ display(HTML(download_link))
422
+ else:
423
+ return output.getvalue()
270
424
 
271
- """
272
- import pandas as pd
425
+ def download_link(self, pretty_labels: Optional[dict] = None) -> str:
426
+ """Return a download link for the results.
273
427
 
274
- conn = self._db(remove_prefix=remove_prefix)
275
- df = pd.read_sql_query(query, conn)
428
+ :param pretty_labels: A dictionary of pretty labels for the columns.
276
429
 
277
- # Transpose the DataFrame if transpose is True
278
- if transpose or transpose_by:
279
- df = pd.DataFrame(df)
280
- if transpose_by:
281
- df = df.set_index(transpose_by)
282
- else:
283
- df = df.set_index(df.columns[0])
284
- df = df.transpose()
285
- from edsl.results.Dataset import Dataset
430
+ >>> from edsl.results import Results
431
+ >>> r = Results.example()
432
+ >>> r.select('how_feeling').download_link()
433
+ '<a href="data:file/csv;base64,YW5zd2VyLmhvd19mZWVsaW5nDQpPSw0KR3JlYXQNClRlcnJpYmxlDQpPSw0K" download="my_data.csv">Download CSV file</a>'
434
+ """
435
+ import base64
286
436
 
287
- return Dataset.from_pandas_dataframe(df)
437
+ csv_string = self.to_csv(pretty_labels=pretty_labels)
438
+ b64 = base64.b64encode(csv_string.encode()).decode()
439
+ return f'<a href="data:file/csv;base64,{b64}" download="my_data.csv">Download CSV file</a>'
288
440
 
289
441
  def to_pandas(
290
442
  self, remove_prefix: bool = False, lists_as_strings=False
@@ -295,6 +447,19 @@ class DatasetExportMixin:
295
447
 
296
448
  """
297
449
  return self._to_pandas_strings(remove_prefix)
450
+ # if lists_as_strings:
451
+ # return self._to_pandas_strings(remove_prefix=remove_prefix)
452
+
453
+ # import pandas as pd
454
+
455
+ # df = pd.DataFrame(self.data)
456
+
457
+ # if remove_prefix:
458
+ # # Optionally remove prefixes from column names
459
+ # df.columns = [col.split(".")[-1] for col in df.columns]
460
+
461
+ # df_sorted = df.sort_index(axis=1) # Sort columns alphabetically
462
+ # return df_sorted
298
463
 
299
464
  def _to_pandas_strings(self, remove_prefix: bool = False) -> "pd.DataFrame":
300
465
  """Convert the results to a pandas DataFrame.
@@ -313,32 +478,12 @@ class DatasetExportMixin:
313
478
 
314
479
  import pandas as pd
315
480
 
316
- csv_string = self.to_csv(remove_prefix=remove_prefix).text
481
+ csv_string = self.to_csv(remove_prefix=remove_prefix)
317
482
  csv_buffer = io.StringIO(csv_string)
318
483
  df = pd.read_csv(csv_buffer)
319
484
  # df_sorted = df.sort_index(axis=1) # Sort columns alphabetically
320
485
  return df
321
486
 
322
- def to_polars(
323
- self, remove_prefix: bool = False, lists_as_strings=False
324
- ) -> "pl.DataFrame":
325
- """Convert the results to a Polars DataFrame.
326
-
327
- :param remove_prefix: Whether to remove the prefix from the column names.
328
- """
329
- return self._to_polars_strings(remove_prefix)
330
-
331
- def _to_polars_strings(self, remove_prefix: bool = False) -> "pl.DataFrame":
332
- """Convert the results to a Polars DataFrame.
333
-
334
- :param remove_prefix: Whether to remove the prefix from the column names.
335
- """
336
- import polars as pl
337
-
338
- csv_string = self.to_csv(remove_prefix=remove_prefix).text
339
- df = pl.read_csv(io.StringIO(csv_string))
340
- return df
341
-
342
487
  def to_scenario_list(self, remove_prefix: bool = True) -> list[dict]:
343
488
  """Convert the results to a list of dictionaries, one per scenario.
344
489
 
@@ -349,14 +494,14 @@ class DatasetExportMixin:
349
494
  >>> r.select('how_feeling').to_scenario_list()
350
495
  ScenarioList([Scenario({'how_feeling': 'OK'}), Scenario({'how_feeling': 'Great'}), Scenario({'how_feeling': 'Terrible'}), Scenario({'how_feeling': 'OK'})])
351
496
  """
352
- from edsl.scenarios.ScenarioList import ScenarioList
353
- from edsl.scenarios.Scenario import Scenario
497
+ from edsl import ScenarioList, Scenario
354
498
 
355
499
  list_of_dicts = self.to_dicts(remove_prefix=remove_prefix)
356
500
  scenarios = []
357
501
  for d in list_of_dicts:
358
502
  scenarios.append(Scenario(d))
359
503
  return ScenarioList(scenarios)
504
+ # return ScenarioList([Scenario(d) for d in list_of_dicts])
360
505
 
361
506
  def to_agent_list(self, remove_prefix: bool = True):
362
507
  """Convert the results to a list of dictionaries, one per agent.
@@ -368,8 +513,7 @@ class DatasetExportMixin:
368
513
  >>> r.select('how_feeling').to_agent_list()
369
514
  AgentList([Agent(traits = {'how_feeling': 'OK'}), Agent(traits = {'how_feeling': 'Great'}), Agent(traits = {'how_feeling': 'Terrible'}), Agent(traits = {'how_feeling': 'OK'})])
370
515
  """
371
- from edsl.agents import Agent
372
- from edsl.agents.AgentList import AgentList
516
+ from edsl import AgentList, Agent
373
517
 
374
518
  list_of_dicts = self.to_dicts(remove_prefix=remove_prefix)
375
519
  agents = []
@@ -377,11 +521,6 @@ class DatasetExportMixin:
377
521
  if "name" in d:
378
522
  d["agent_name"] = d.pop("name")
379
523
  agents.append(Agent(d, name=d["agent_name"]))
380
- if "agent_parameters" in d:
381
- agent_parameters = d.pop("agent_parameters")
382
- agent_name = agent_parameters.get("name", None)
383
- instruction = agent_parameters.get("instruction", None)
384
- agents.append(Agent(d, name=agent_name, instruction=instruction))
385
524
  else:
386
525
  agents.append(Agent(d))
387
526
  return AgentList(agents)
@@ -469,9 +608,7 @@ class DatasetExportMixin:
469
608
  new_list.append(item)
470
609
  list_to_return = new_list
471
610
 
472
- from edsl.utilities.PrettyList import PrettyList
473
-
474
- return PrettyList(list_to_return)
611
+ return list_to_return
475
612
 
476
613
  def html(
477
614
  self,
@@ -521,10 +658,8 @@ class DatasetExportMixin:
521
658
  >>> r = Results.example()
522
659
  >>> r.select('how_feeling').tally('answer.how_feeling', output = "dict")
523
660
  {'OK': 2, 'Great': 1, 'Terrible': 1}
524
- >>> from edsl.results.Dataset import Dataset
525
- >>> expected = Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible']}, {'count': [2, 1, 1]}])
526
- >>> r.select('how_feeling').tally('answer.how_feeling', output = "Dataset") == expected
527
- True
661
+ >>> r.select('how_feeling').tally('answer.how_feeling', output = "Dataset")
662
+ Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible']}, {'count': [2, 1, 1]}])
528
663
  """
529
664
  from collections import Counter
530
665
 
@@ -556,6 +691,8 @@ class DatasetExportMixin:
556
691
  if top_n is not None:
557
692
  sorted_tally = dict(list(sorted_tally.items())[:top_n])
558
693
 
694
+ import warnings
695
+ import textwrap
559
696
  from edsl.results.Dataset import Dataset
560
697
 
561
698
  if output == "dict":
@@ -1,4 +1,8 @@
1
1
  from typing import Dict, List, Any, Optional, List
2
+ from docx import Document
3
+ from docx.shared import Inches, Pt
4
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
5
+ from docx.enum.style import WD_STYLE_TYPE
2
6
 
3
7
 
4
8
  def is_hashable(v):
@@ -94,11 +98,6 @@ class Tree:
94
98
  if filename is None:
95
99
  filename = "tree_structure.docx"
96
100
 
97
- from docx import Document
98
- from docx.shared import Inches, Pt
99
- from docx.enum.text import WD_ALIGN_PARAGRAPH
100
- from docx.enum.style import WD_STYLE_TYPE
101
-
102
101
  doc = Document()
103
102
 
104
103
  # Create styles for headings
@@ -116,29 +115,10 @@ class Tree:
116
115
  body_style.font.size = Pt(11)
117
116
 
118
117
  self._add_to_docx(doc, self.root, 0)
119
- import base64
120
- from io import BytesIO
121
- import base64
122
-
123
- # Save document to bytes buffer
124
- doc_buffer = BytesIO()
125
- doc.save(doc_buffer)
126
- doc_buffer.seek(0)
127
-
128
- base64_string = base64.b64encode(doc_buffer.getvalue()).decode("utf-8")
129
- from edsl.scenarios.FileStore import FileStore
130
-
131
- # Create and return FileStore instance
132
- return FileStore(
133
- path="tree_structure.docx", # Default name
134
- mime_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
135
- binary=True,
136
- suffix="docx",
137
- base64_string=base64_string,
138
- )
139
- # doc.save(filename)
140
- # from edsl.utilities.utilities import file_notice
141
- # file_notice(filename)
118
+ doc.save(filename)
119
+ from edsl.utilities.utilities import file_notice
120
+
121
+ file_notice(filename)
142
122
 
143
123
  def _repr_html_(self):
144
124
  """Returns an interactive HTML representation of the tree with collapsible sections."""