edsl 0.1.39.dev2__py3-none-any.whl → 0.1.39.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 (334) hide show
  1. edsl/Base.py +332 -385
  2. edsl/BaseDiff.py +260 -260
  3. edsl/TemplateLoader.py +24 -24
  4. edsl/__init__.py +49 -57
  5. edsl/__version__.py +1 -1
  6. edsl/agents/Agent.py +867 -1079
  7. edsl/agents/AgentList.py +413 -551
  8. edsl/agents/Invigilator.py +233 -285
  9. edsl/agents/InvigilatorBase.py +270 -254
  10. edsl/agents/PromptConstructor.py +354 -252
  11. edsl/agents/__init__.py +3 -2
  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 +157 -177
  27. edsl/conversation/Conversation.py +290 -290
  28. edsl/conversation/car_buying.py +58 -59
  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 +1028 -1090
  35. edsl/coop/utils.py +131 -131
  36. edsl/data/Cache.py +555 -562
  37. edsl/data/CacheEntry.py +233 -230
  38. edsl/data/CacheHandler.py +149 -170
  39. edsl/data/RemoteCacheSync.py +78 -78
  40. edsl/data/SQLiteDict.py +292 -292
  41. edsl/data/__init__.py +4 -5
  42. edsl/data/orm.py +10 -10
  43. edsl/data_transfer_models.py +73 -74
  44. edsl/enums.py +175 -195
  45. edsl/exceptions/BaseException.py +21 -21
  46. edsl/exceptions/__init__.py +54 -54
  47. edsl/exceptions/agents.py +42 -54
  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 -109
  57. edsl/exceptions/results.py +29 -29
  58. edsl/exceptions/scenarios.py +22 -29
  59. edsl/exceptions/surveys.py +37 -37
  60. edsl/inference_services/AnthropicService.py +87 -84
  61. edsl/inference_services/AwsBedrock.py +120 -118
  62. edsl/inference_services/AzureAI.py +217 -215
  63. edsl/inference_services/DeepInfraService.py +18 -18
  64. edsl/inference_services/GoogleService.py +148 -139
  65. edsl/inference_services/GroqService.py +20 -20
  66. edsl/inference_services/InferenceServiceABC.py +147 -80
  67. edsl/inference_services/InferenceServicesCollection.py +97 -122
  68. edsl/inference_services/MistralAIService.py +123 -120
  69. edsl/inference_services/OllamaService.py +18 -18
  70. edsl/inference_services/OpenAIService.py +224 -221
  71. edsl/inference_services/PerplexityService.py +163 -160
  72. edsl/inference_services/TestService.py +89 -92
  73. edsl/inference_services/TogetherAIService.py +170 -170
  74. edsl/inference_services/models_available_cache.py +118 -118
  75. edsl/inference_services/rate_limits_cache.py +25 -25
  76. edsl/inference_services/registry.py +41 -41
  77. edsl/inference_services/write_available.py +10 -10
  78. edsl/jobs/Answers.py +56 -43
  79. edsl/jobs/Jobs.py +898 -757
  80. edsl/jobs/JobsChecks.py +147 -172
  81. edsl/jobs/JobsPrompts.py +268 -270
  82. edsl/jobs/JobsRemoteInferenceHandler.py +239 -287
  83. edsl/jobs/__init__.py +1 -1
  84. edsl/jobs/buckets/BucketCollection.py +63 -104
  85. edsl/jobs/buckets/ModelBuckets.py +65 -65
  86. edsl/jobs/buckets/TokenBucket.py +251 -283
  87. edsl/jobs/interviews/Interview.py +661 -358
  88. edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
  89. edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
  90. edsl/jobs/interviews/InterviewStatistic.py +63 -63
  91. edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
  92. edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
  93. edsl/jobs/interviews/InterviewStatusLog.py +92 -92
  94. edsl/jobs/interviews/ReportErrors.py +66 -66
  95. edsl/jobs/interviews/interview_status_enum.py +9 -9
  96. edsl/jobs/runners/JobsRunnerAsyncio.py +466 -421
  97. edsl/jobs/runners/JobsRunnerStatus.py +330 -330
  98. edsl/jobs/tasks/QuestionTaskCreator.py +242 -244
  99. edsl/jobs/tasks/TaskCreators.py +64 -64
  100. edsl/jobs/tasks/TaskHistory.py +450 -449
  101. edsl/jobs/tasks/TaskStatusLog.py +23 -23
  102. edsl/jobs/tasks/task_status_enum.py +163 -161
  103. edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
  104. edsl/jobs/tokens/TokenUsage.py +34 -34
  105. edsl/language_models/KeyLookup.py +30 -0
  106. edsl/language_models/LanguageModel.py +668 -571
  107. edsl/language_models/ModelList.py +155 -153
  108. edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
  109. edsl/language_models/__init__.py +3 -2
  110. edsl/language_models/fake_openai_call.py +15 -15
  111. edsl/language_models/fake_openai_service.py +61 -61
  112. edsl/language_models/registry.py +190 -180
  113. edsl/language_models/repair.py +156 -156
  114. edsl/language_models/unused/ReplicateBase.py +83 -0
  115. edsl/language_models/utilities.py +64 -65
  116. edsl/notebooks/Notebook.py +258 -263
  117. edsl/notebooks/__init__.py +1 -1
  118. edsl/prompts/Prompt.py +362 -352
  119. edsl/prompts/__init__.py +2 -2
  120. edsl/questions/AnswerValidatorMixin.py +289 -334
  121. edsl/questions/QuestionBase.py +664 -509
  122. edsl/questions/QuestionBaseGenMixin.py +161 -165
  123. edsl/questions/QuestionBasePromptsMixin.py +217 -221
  124. edsl/questions/QuestionBudget.py +227 -227
  125. edsl/questions/QuestionCheckBox.py +359 -359
  126. edsl/questions/QuestionExtract.py +182 -182
  127. edsl/questions/QuestionFreeText.py +114 -113
  128. edsl/questions/QuestionFunctional.py +166 -166
  129. edsl/questions/QuestionList.py +231 -229
  130. edsl/questions/QuestionMultipleChoice.py +286 -330
  131. edsl/questions/QuestionNumerical.py +153 -151
  132. edsl/questions/QuestionRank.py +324 -314
  133. edsl/questions/Quick.py +41 -41
  134. edsl/questions/RegisterQuestionsMeta.py +71 -71
  135. edsl/questions/ResponseValidatorABC.py +174 -200
  136. edsl/questions/SimpleAskMixin.py +73 -74
  137. edsl/questions/__init__.py +26 -27
  138. edsl/questions/compose_questions.py +98 -98
  139. edsl/questions/decorators.py +21 -21
  140. edsl/questions/derived/QuestionLikertFive.py +76 -76
  141. edsl/questions/derived/QuestionLinearScale.py +87 -90
  142. edsl/questions/derived/QuestionTopK.py +93 -93
  143. edsl/questions/derived/QuestionYesNo.py +82 -82
  144. edsl/questions/descriptors.py +413 -427
  145. edsl/questions/prompt_templates/question_budget.jinja +13 -13
  146. edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
  147. edsl/questions/prompt_templates/question_extract.jinja +11 -11
  148. edsl/questions/prompt_templates/question_free_text.jinja +3 -3
  149. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
  150. edsl/questions/prompt_templates/question_list.jinja +17 -17
  151. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
  152. edsl/questions/prompt_templates/question_numerical.jinja +36 -36
  153. edsl/questions/question_registry.py +177 -177
  154. edsl/questions/settings.py +12 -12
  155. edsl/questions/templates/budget/answering_instructions.jinja +7 -7
  156. edsl/questions/templates/budget/question_presentation.jinja +7 -7
  157. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
  158. edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
  159. edsl/questions/templates/extract/answering_instructions.jinja +7 -7
  160. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
  161. edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
  162. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
  163. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
  164. edsl/questions/templates/list/answering_instructions.jinja +3 -3
  165. edsl/questions/templates/list/question_presentation.jinja +5 -5
  166. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
  167. edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
  168. edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
  169. edsl/questions/templates/numerical/question_presentation.jinja +6 -6
  170. edsl/questions/templates/rank/answering_instructions.jinja +11 -11
  171. edsl/questions/templates/rank/question_presentation.jinja +15 -15
  172. edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
  173. edsl/questions/templates/top_k/question_presentation.jinja +22 -22
  174. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
  175. edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
  176. edsl/results/CSSParameterizer.py +108 -108
  177. edsl/results/Dataset.py +424 -587
  178. edsl/results/DatasetExportMixin.py +731 -653
  179. edsl/results/DatasetTree.py +275 -295
  180. edsl/results/Result.py +465 -451
  181. edsl/results/Results.py +1165 -1172
  182. edsl/results/ResultsDBMixin.py +238 -0
  183. edsl/results/ResultsExportMixin.py +43 -45
  184. edsl/results/ResultsFetchMixin.py +33 -33
  185. edsl/results/ResultsGGMixin.py +121 -121
  186. edsl/results/ResultsToolsMixin.py +98 -98
  187. edsl/results/Selector.py +135 -145
  188. edsl/results/TableDisplay.py +198 -125
  189. edsl/results/__init__.py +2 -2
  190. edsl/results/table_display.css +77 -77
  191. edsl/results/tree_explore.py +115 -115
  192. edsl/scenarios/FileStore.py +632 -511
  193. edsl/scenarios/Scenario.py +601 -498
  194. edsl/scenarios/ScenarioHtmlMixin.py +64 -65
  195. edsl/scenarios/ScenarioJoin.py +127 -131
  196. edsl/scenarios/ScenarioList.py +1287 -1430
  197. edsl/scenarios/ScenarioListExportMixin.py +52 -45
  198. edsl/scenarios/ScenarioListPdfMixin.py +261 -239
  199. edsl/scenarios/__init__.py +4 -3
  200. edsl/shared.py +1 -1
  201. edsl/study/ObjectEntry.py +173 -173
  202. edsl/study/ProofOfWork.py +113 -113
  203. edsl/study/SnapShot.py +80 -80
  204. edsl/study/Study.py +528 -521
  205. edsl/study/__init__.py +4 -4
  206. edsl/surveys/DAG.py +148 -148
  207. edsl/surveys/Memory.py +31 -31
  208. edsl/surveys/MemoryPlan.py +244 -244
  209. edsl/surveys/Rule.py +326 -327
  210. edsl/surveys/RuleCollection.py +387 -385
  211. edsl/surveys/Survey.py +1801 -1229
  212. edsl/surveys/SurveyCSS.py +261 -273
  213. edsl/surveys/SurveyExportMixin.py +259 -259
  214. edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +179 -181
  215. edsl/surveys/SurveyQualtricsImport.py +284 -284
  216. edsl/surveys/__init__.py +3 -5
  217. edsl/surveys/base.py +53 -53
  218. edsl/surveys/descriptors.py +56 -60
  219. edsl/surveys/instructions/ChangeInstruction.py +49 -48
  220. edsl/surveys/instructions/Instruction.py +65 -56
  221. edsl/surveys/instructions/InstructionCollection.py +77 -82
  222. edsl/templates/error_reporting/base.html +23 -23
  223. edsl/templates/error_reporting/exceptions_by_model.html +34 -34
  224. edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
  225. edsl/templates/error_reporting/exceptions_by_type.html +16 -16
  226. edsl/templates/error_reporting/interview_details.html +115 -115
  227. edsl/templates/error_reporting/interviews.html +19 -19
  228. edsl/templates/error_reporting/overview.html +4 -4
  229. edsl/templates/error_reporting/performance_plot.html +1 -1
  230. edsl/templates/error_reporting/report.css +73 -73
  231. edsl/templates/error_reporting/report.html +117 -117
  232. edsl/templates/error_reporting/report.js +25 -25
  233. edsl/tools/__init__.py +1 -1
  234. edsl/tools/clusters.py +192 -192
  235. edsl/tools/embeddings.py +27 -27
  236. edsl/tools/embeddings_plotting.py +118 -118
  237. edsl/tools/plotting.py +112 -112
  238. edsl/tools/summarize.py +18 -18
  239. edsl/utilities/SystemInfo.py +28 -28
  240. edsl/utilities/__init__.py +22 -22
  241. edsl/utilities/ast_utilities.py +25 -25
  242. edsl/utilities/data/Registry.py +6 -6
  243. edsl/utilities/data/__init__.py +1 -1
  244. edsl/utilities/data/scooter_results.json +1 -1
  245. edsl/utilities/decorators.py +77 -77
  246. edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
  247. edsl/utilities/interface.py +627 -627
  248. edsl/utilities/naming_utilities.py +263 -263
  249. edsl/utilities/repair_functions.py +28 -28
  250. edsl/utilities/restricted_python.py +70 -70
  251. edsl/utilities/utilities.py +424 -436
  252. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/LICENSE +21 -21
  253. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/METADATA +10 -12
  254. edsl-0.1.39.dev3.dist-info/RECORD +277 -0
  255. edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
  256. edsl/agents/QuestionOptionProcessor.py +0 -172
  257. edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
  258. edsl/coop/CoopFunctionsMixin.py +0 -15
  259. edsl/coop/ExpectedParrotKeyHandler.py +0 -125
  260. edsl/exceptions/inference_services.py +0 -5
  261. edsl/inference_services/AvailableModelCacheHandler.py +0 -184
  262. edsl/inference_services/AvailableModelFetcher.py +0 -209
  263. edsl/inference_services/ServiceAvailability.py +0 -135
  264. edsl/inference_services/data_structures.py +0 -62
  265. edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -188
  266. edsl/jobs/FetchInvigilator.py +0 -40
  267. edsl/jobs/InterviewTaskManager.py +0 -98
  268. edsl/jobs/InterviewsConstructor.py +0 -48
  269. edsl/jobs/JobsComponentConstructor.py +0 -189
  270. edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
  271. edsl/jobs/RequestTokenEstimator.py +0 -30
  272. edsl/jobs/buckets/TokenBucketAPI.py +0 -211
  273. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  274. edsl/jobs/decorators.py +0 -35
  275. edsl/jobs/jobs_status_enums.py +0 -9
  276. edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
  277. edsl/language_models/ComputeCost.py +0 -63
  278. edsl/language_models/PriceManager.py +0 -127
  279. edsl/language_models/RawResponseHandler.py +0 -106
  280. edsl/language_models/ServiceDataSources.py +0 -0
  281. edsl/language_models/key_management/KeyLookup.py +0 -63
  282. edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
  283. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  284. edsl/language_models/key_management/__init__.py +0 -0
  285. edsl/language_models/key_management/models.py +0 -131
  286. edsl/notebooks/NotebookToLaTeX.py +0 -142
  287. edsl/questions/ExceptionExplainer.py +0 -77
  288. edsl/questions/HTMLQuestion.py +0 -103
  289. edsl/questions/LoopProcessor.py +0 -149
  290. edsl/questions/QuestionMatrix.py +0 -265
  291. edsl/questions/ResponseValidatorFactory.py +0 -28
  292. edsl/questions/templates/matrix/__init__.py +0 -1
  293. edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
  294. edsl/questions/templates/matrix/question_presentation.jinja +0 -20
  295. edsl/results/MarkdownToDocx.py +0 -122
  296. edsl/results/MarkdownToPDF.py +0 -111
  297. edsl/results/TextEditor.py +0 -50
  298. edsl/results/smart_objects.py +0 -96
  299. edsl/results/table_data_class.py +0 -12
  300. edsl/results/table_renderers.py +0 -118
  301. edsl/scenarios/ConstructDownloadLink.py +0 -109
  302. edsl/scenarios/DirectoryScanner.py +0 -96
  303. edsl/scenarios/DocumentChunker.py +0 -102
  304. edsl/scenarios/DocxScenario.py +0 -16
  305. edsl/scenarios/PdfExtractor.py +0 -40
  306. edsl/scenarios/ScenarioSelector.py +0 -156
  307. edsl/scenarios/file_methods.py +0 -85
  308. edsl/scenarios/handlers/__init__.py +0 -13
  309. edsl/scenarios/handlers/csv.py +0 -38
  310. edsl/scenarios/handlers/docx.py +0 -76
  311. edsl/scenarios/handlers/html.py +0 -37
  312. edsl/scenarios/handlers/json.py +0 -111
  313. edsl/scenarios/handlers/latex.py +0 -5
  314. edsl/scenarios/handlers/md.py +0 -51
  315. edsl/scenarios/handlers/pdf.py +0 -68
  316. edsl/scenarios/handlers/png.py +0 -39
  317. edsl/scenarios/handlers/pptx.py +0 -105
  318. edsl/scenarios/handlers/py.py +0 -294
  319. edsl/scenarios/handlers/sql.py +0 -313
  320. edsl/scenarios/handlers/sqlite.py +0 -149
  321. edsl/scenarios/handlers/txt.py +0 -33
  322. edsl/surveys/ConstructDAG.py +0 -92
  323. edsl/surveys/EditSurvey.py +0 -221
  324. edsl/surveys/InstructionHandler.py +0 -100
  325. edsl/surveys/MemoryManagement.py +0 -72
  326. edsl/surveys/RuleManager.py +0 -172
  327. edsl/surveys/Simulator.py +0 -75
  328. edsl/surveys/SurveyToApp.py +0 -141
  329. edsl/utilities/PrettyList.py +0 -56
  330. edsl/utilities/is_notebook.py +0 -18
  331. edsl/utilities/is_valid_variable_name.py +0 -11
  332. edsl/utilities/remove_edsl_version.py +0 -24
  333. edsl-0.1.39.dev2.dist-info/RECORD +0 -352
  334. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/WHEEL +0 -0
@@ -1,111 +0,0 @@
1
- from typing import Optional
2
- import subprocess
3
- import os
4
- from pathlib import Path
5
- import tempfile
6
-
7
-
8
- class MarkdownToPDF:
9
- def __init__(self, markdown_content: str, filename: Optional[str] = None):
10
- """
11
- Initialize the converter with markdown content.
12
-
13
- Args:
14
- markdown_content (str): The markdown content to be converted
15
- """
16
- self.markdown_content = markdown_content
17
- self.filename = filename
18
- self._check_pandoc()
19
- # self.convert()
20
-
21
- def _check_pandoc(self):
22
- """Check if pandoc is installed and accessible."""
23
- try:
24
- subprocess.run(["pandoc", "--version"], capture_output=True, check=True)
25
- except (subprocess.CalledProcessError, FileNotFoundError):
26
- raise RuntimeError(
27
- "Pandoc is not installed or not found in PATH. "
28
- "Please install pandoc before using this converter."
29
- )
30
-
31
- def convert(self, output_path: str, **options) -> bool:
32
- """
33
- Convert the markdown content to PDF.
34
-
35
- Args:
36
- output_path (str): Path where the PDF should be saved
37
- **options: Additional conversion options
38
- margin (str): Page margin (default: "1in")
39
- font_size (str): Font size (default: "12pt")
40
- toc (bool): Include table of contents (default: False)
41
- number_sections (bool): Number sections (default: False)
42
- highlight_style (str): Code highlighting style (default: "tango")
43
-
44
- Returns:
45
- bool: True if conversion was successful, False otherwise
46
- """
47
- # Ensure output directory exists
48
- output_path = Path(output_path)
49
- output_path.parent.mkdir(parents=True, exist_ok=True)
50
-
51
- # Build pandoc command with default options
52
- cmd = [
53
- "pandoc",
54
- "-f",
55
- "markdown",
56
- "-o",
57
- str(output_path),
58
- "--pdf-engine=xelatex",
59
- "--variable",
60
- f'geometry:margin={options.get("margin", "1in")}',
61
- "--variable",
62
- f'fontsize={options.get("font_size", "12pt")}',
63
- ]
64
-
65
- # Add font only if specifically provided
66
- if "font" in options:
67
- cmd.extend(["--variable", f'mainfont={options["font"]}'])
68
-
69
- # Add optional parameters
70
- if options.get("toc", False):
71
- cmd.append("--toc")
72
-
73
- if options.get("number_sections", False):
74
- cmd.append("--number-sections")
75
-
76
- if "highlight_style" in options:
77
- cmd.extend(["--highlight-style", options["highlight_style"]])
78
-
79
- try:
80
- # Run pandoc command
81
- result = subprocess.run(
82
- cmd,
83
- input=self.markdown_content,
84
- text=True,
85
- capture_output=True,
86
- check=True,
87
- )
88
- return True
89
- except subprocess.CalledProcessError as e:
90
- print(f"Error converting markdown to PDF: {e.stderr}")
91
- return False
92
-
93
- def preview(self) -> str:
94
- """
95
- Generate a temporary PDF and return its path.
96
-
97
- Returns:
98
- str: Path to the temporary PDF file
99
- """
100
- temp_dir = tempfile.mkdtemp()
101
- if self.filename:
102
- temp_pdf = os.path.join(temp_dir, f"{self.filename}.pdf")
103
- else:
104
- temp_pdf = os.path.join(temp_dir, "preview.pdf")
105
-
106
- if self.convert(temp_pdf):
107
- from edsl.scenarios.FileStore import FileStore
108
-
109
- return FileStore(temp_pdf)
110
-
111
- return None
@@ -1,50 +0,0 @@
1
- try:
2
- import gradio as gr
3
- except ImportError:
4
- print("Gradio is not installed. Please install it using `pip install gradio`")
5
-
6
- import time
7
-
8
-
9
- class TextEditor:
10
- def __init__(self, initial_text=""):
11
- self.text = initial_text
12
- self._text_saved = False
13
-
14
- def save_text(self, new_text):
15
- self.text = new_text
16
- self._text_saved = True
17
- return "Text saved successfully!"
18
-
19
- def edit_gui(self):
20
- js_code = """
21
- async (text) => {
22
- await navigator.clipboard.writeText(text);
23
- return "Copied to clipboard!";
24
- }
25
- """
26
-
27
- with gr.Blocks() as interface:
28
- text_area = gr.Textbox(
29
- value=self.text, lines=10, label="Edit Text", placeholder="Type here..."
30
- )
31
-
32
- with gr.Row():
33
- save_btn = gr.Button("Save")
34
- copy_btn = gr.Button("Copy to Clipboard")
35
-
36
- output = gr.Textbox(label="Status")
37
-
38
- save_btn.click(fn=self.save_text, inputs=[text_area], outputs=[output])
39
-
40
- # Add copy functionality
41
- copy_btn.click(
42
- fn=None, inputs=text_area, outputs=output, api_name=False, js=js_code
43
- )
44
-
45
- interface.launch(share=False, prevent_thread_lock=True)
46
-
47
- while not self._text_saved:
48
- time.sleep(0.1)
49
-
50
- return self.text
@@ -1,96 +0,0 @@
1
- from typing import Optional
2
-
3
-
4
- class SmartInt(int):
5
- pass
6
-
7
-
8
- class SmartFloat(float):
9
- pass
10
-
11
-
12
- class SmartStr(str):
13
- def clipboard(self) -> None:
14
- try:
15
- import pyperclip
16
- except ImportError:
17
- print(
18
- "pyperclip is not installed. Run `pip install pyperclip` to install it."
19
- )
20
- return None
21
-
22
- pyperclip.copy(self)
23
- print("Text copied to clipboard.")
24
-
25
- def write(self, filename: str):
26
- with open(filename, "w") as f:
27
- f.write(str(self))
28
- return None
29
-
30
- def _repr_html_(self):
31
- pass
32
-
33
- def markdown(self):
34
- return SmartMarkdown(self)
35
-
36
- def pdf(self, filename: Optional[str] = None): # Markdown will have this as well
37
- # renders the markdown as a pdf that can be downloaded
38
- from edsl.results.MarkdownToPDF import MarkdownToPDF
39
-
40
- return MarkdownToPDF(self, filename).preview()
41
-
42
- def docx(self, filename: Optional[str] = None):
43
- # renders the markdown as a docx that can be downloaded
44
- from edsl.results.MarkdownToDocx import MarkdownToDocx
45
-
46
- return MarkdownToDocx(self, filename).preview()
47
-
48
- def edit(self):
49
- from edsl.results.TextEditor import TextEditor
50
-
51
- editor = TextEditor(self)
52
- self = self.__class__(editor.edit_gui())
53
- # print(f"Updated text: {self}")
54
-
55
-
56
- class SmartMarkdown(SmartStr):
57
- def _repr_markdown_(self):
58
- return self
59
-
60
- def _repr_html_(self):
61
- from IPython.display import Markdown, display
62
-
63
- display(Markdown(self))
64
-
65
-
66
- class SmartLaTeX(SmartStr):
67
- def _repr_html_(self):
68
- print(self)
69
-
70
- def pdf(self, filename: Optional[str] = None):
71
- from edsl.results.LaTeXToPDF import LaTeXToPDF
72
-
73
- return LaTeXToPDF(self, filename).preview()
74
-
75
- def docx(self, filename: Optional[str] = None):
76
- from edsl.results.LaTeXToDocx import LaTeXToDocx
77
-
78
- return LaTeXToDocx(self, filename).preview()
79
-
80
- def edit(self):
81
- from edsl.results.TextEditor import TextEditor
82
-
83
- editor = TextEditor(self)
84
- self = self.__class__(editor.edit_gui())
85
- # print(f"Updated LaTeX: {self}")
86
-
87
-
88
- class FirstObject:
89
- def __new__(self, value):
90
- if isinstance(value, int):
91
- return SmartInt(value)
92
- if isinstance(value, float):
93
- return SmartFloat(value)
94
- if isinstance(value, str):
95
- return SmartStr(value)
96
- return value
@@ -1,12 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Any, List
3
-
4
-
5
- @dataclass
6
- class TableData:
7
- """Simple data class to hold table information"""
8
-
9
- headers: List[str]
10
- data: List[List[Any]]
11
- parameters: dict = None
12
- raw_data_set: Any = None
@@ -1,118 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from edsl.results.table_data_class import TableData
3
-
4
-
5
- class DataTablesRendererABC(ABC):
6
- def __init__(self, table_data: TableData):
7
- self.table_data = table_data
8
-
9
- @abstractmethod
10
- def render_html(self) -> str:
11
- pass
12
-
13
-
14
- class DataTablesRenderer(DataTablesRendererABC):
15
- """Interactive DataTables renderer implementation"""
16
-
17
- def render_html(self) -> str:
18
- html_template = """
19
- <!DOCTYPE html>
20
- <html>
21
- <head>
22
- <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
23
- <link href="https://cdnjs.cloudflare.com/ajax/libs/datatables.net-bs5/1.13.6/dataTables.bootstrap5.min.css" rel="stylesheet">
24
- <link href="https://cdnjs.cloudflare.com/ajax/libs/datatables.net-buttons-bs5/2.4.1/buttons.bootstrap5.min.css" rel="stylesheet">
25
- <link href="https://cdnjs.cloudflare.com/ajax/libs/datatables.net-responsive-bs5/2.4.1/responsive.bootstrap5.min.css" rel="stylesheet">
26
- <style>
27
- {css}
28
- </style>
29
- </head>
30
- <body>
31
- <div class="container">
32
- <table id="interactive-table" class="table table-striped" style="width:100%">
33
- <thead>
34
- <tr>{header_cells}</tr>
35
- </thead>
36
- <tbody>{body_rows}</tbody>
37
- </table>
38
- </div>
39
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
40
- <script src="https://cdnjs.cloudflare.com/ajax/libs/datatables.net/1.13.6/jquery.dataTables.min.js"></script>
41
- <script src="https://cdnjs.cloudflare.com/ajax/libs/datatables.net-bs5/1.13.6/dataTables.bootstrap5.min.js"></script>
42
- <script>
43
- $(document).ready(function() {{
44
- $('#interactive-table').DataTable({{
45
- pageLength: 10,
46
- lengthMenu: [[5, 10, 25, -1], [5, 10, 25, "All"]],
47
- scrollX: true,
48
- responsive: true,
49
- dom: 'Bfrtip',
50
- buttons: [
51
- {{
52
- extend: 'colvis',
53
- text: 'Show/Hide Columns'
54
- }}
55
- ]
56
- }});
57
- }});
58
- </script>
59
- </body>
60
- </html>
61
- """
62
-
63
- header_cells = "".join(
64
- f"<th>{header}</th>" for header in self.table_data.headers
65
- )
66
- body_rows = ""
67
- for row in self.table_data.data:
68
- body_rows += "<tr>"
69
- body_rows += "".join(f"<td>{cell}</td>" for cell in row)
70
- body_rows += "</tr>"
71
-
72
- parameters = self.table_data.parameters or {}
73
- css = self.get_css()
74
- if hasattr(self, "css_parameterizer"):
75
- css = self.css_parameterizer(css).apply_parameters(parameters)
76
-
77
- return html_template.format(
78
- css=css, header_cells=header_cells, body_rows=body_rows
79
- )
80
-
81
- @classmethod
82
- def get_css(cls) -> str:
83
- """Load CSS content from the file next to this module"""
84
- css_path = Path(__file__).parent / "table_display.css"
85
- return css_path.read_text()
86
-
87
-
88
- class PandasStyleRenderer(DataTablesRendererABC):
89
- """Pandas-based styled renderer implementation"""
90
-
91
- def render_html(self) -> str:
92
- import pandas as pd
93
-
94
- from contextlib import redirect_stderr
95
- import io
96
-
97
- stderr = io.StringIO()
98
- with redirect_stderr(stderr):
99
- if self.table_data.raw_data_set is not None and hasattr(
100
- self.table_data.raw_data_set, "to_pandas"
101
- ):
102
- df = self.table_data.raw_data_set.to_pandas()
103
- else:
104
- df = pd.DataFrame(self.table_data.data, columns=self.table_data.headers)
105
-
106
- styled_df = df.style.set_properties(
107
- **{"text-align": "left"}
108
- ).background_gradient()
109
-
110
- return f"""
111
- <div style="max-height: 500px; overflow-y: auto;">
112
- {styled_df.to_html()}
113
- </div>
114
- """
115
-
116
- @classmethod
117
- def get_css(cls) -> str:
118
- return "" # Pandas styling handles its own CSS
@@ -1,109 +0,0 @@
1
- import os
2
- import mimetypes
3
-
4
-
5
- class ConstructDownloadLink:
6
- """
7
- A class to create HTML download links for FileStore objects.
8
- The links can be displayed in Jupyter notebooks or other web interfaces.
9
- """
10
-
11
- def __init__(self, filestore):
12
- """
13
- Initialize with a FileStore object.
14
-
15
- Args:
16
- filestore: A FileStore object containing the file to be made downloadable
17
- """
18
- self.filestore = filestore
19
-
20
- def create_link(self, custom_filename=None, style=None):
21
- from IPython.display import HTML
22
-
23
- html = self.html_create_link(custom_filename, style)
24
- return HTML(html)
25
-
26
- def html_create_link(self, custom_filename=None, style=None):
27
- """
28
- Create an HTML download link for the file.
29
-
30
- Args:
31
- custom_filename (str, optional): Custom name for the downloaded file.
32
- If None, uses original filename.
33
- style (dict, optional): Custom CSS styles for the download button.
34
- If None, uses default styling.
35
-
36
- Returns:
37
- IPython.display.HTML: HTML object containing the download link
38
- """
39
-
40
- # Get filename from path or use custom filename
41
- original_filename = os.path.basename(self.filestore.path)
42
- filename = custom_filename or original_filename
43
-
44
- # Use the base64 string already stored in FileStore
45
- b64_data = self.filestore.base64_string
46
-
47
- # Use mime type from FileStore or guess it
48
- mime_type = self.filestore.mime_type
49
-
50
- # Default style if none provided
51
- default_style = {
52
- "background-color": "#4CAF50",
53
- "color": "white",
54
- "padding": "10px 20px",
55
- "text-decoration": "none",
56
- "border-radius": "4px",
57
- "display": "inline-block",
58
- "margin": "10px 0",
59
- "font-family": "sans-serif",
60
- "cursor": "pointer",
61
- }
62
-
63
- button_style = style or default_style
64
- style_str = "; ".join(f"{k}: {v}" for k, v in button_style.items())
65
-
66
- html = f"""
67
- <a download="{filename}"
68
- href="data:{mime_type};base64,{b64_data}"
69
- style="{style_str}">
70
- Download {filename}
71
- </a>
72
- """
73
- return html
74
-
75
- def create_multiple_links(self, files, custom_filenames=None, style=None):
76
- """
77
- Create multiple download links at once.
78
- Useful when you want to provide different versions of the same file
79
- or related files together.
80
-
81
- Args:
82
- files (list): List of FileStore objects
83
- custom_filenames (list, optional): List of custom filenames for downloads
84
- style (dict, optional): Custom CSS styles for the download buttons
85
-
86
- Returns:
87
- IPython.display.HTML: HTML object containing all download links
88
- """
89
- if custom_filenames is None:
90
- custom_filenames = [None] * len(files)
91
-
92
- html_parts = []
93
- for file_obj, custom_name in zip(files, custom_filenames):
94
- link_creator = ConstructDownloadLink(file_obj)
95
- html_parts.append(
96
- link_creator.create_link(
97
- custom_filename=custom_name, style=style
98
- )._repr_html_()
99
- )
100
-
101
- return HTML(
102
- '<div style="display: flex; gap: 10px;">' + "".join(html_parts) + "</div>"
103
- )
104
-
105
-
106
- if __name__ == "__main__":
107
- import doctest
108
-
109
- doctest.testmod()
@@ -1,96 +0,0 @@
1
- # directory_scanner.py
2
- from dataclasses import dataclass
3
- from typing import Optional, List, Iterator, TypeVar, Generic, Callable, Any
4
- import os
5
-
6
- T = TypeVar("T")
7
-
8
-
9
- @dataclass
10
- class DirectoryScanner:
11
- """
12
- Scanner for finding files in a directory based on various criteria.
13
- """
14
-
15
- directory_path: str
16
-
17
- def scan(
18
- self,
19
- factory: Callable[[str], T],
20
- recursive: bool = False,
21
- suffix_allow_list: Optional[List[str]] = None,
22
- suffix_exclude_list: Optional[List[str]] = None,
23
- example_suffix: Optional[str] = None,
24
- include_no_extension: bool = True,
25
- ) -> List[T]:
26
- """
27
- Eagerly scan directory and return list of objects created by factory.
28
-
29
- Args:
30
- factory: Callable that creates objects from file paths
31
- recursive: If True, recursively traverse subdirectories
32
- suffix_allow_list: List of allowed file extensions (without dots)
33
- suffix_exclude_list: List of excluded file extensions (takes precedence over allow list)
34
- example_suffix: If provided, only include files with this example suffix
35
- include_no_extension: Whether to include files without extensions
36
- """
37
- return list(
38
- self.iter_scan(
39
- factory,
40
- recursive=recursive,
41
- suffix_allow_list=suffix_allow_list,
42
- suffix_exclude_list=suffix_exclude_list,
43
- example_suffix=example_suffix,
44
- include_no_extension=include_no_extension,
45
- )
46
- )
47
-
48
- def iter_scan(
49
- self,
50
- factory: Callable[[str], T],
51
- recursive: bool = False,
52
- suffix_allow_list: Optional[List[str]] = None,
53
- suffix_exclude_list: Optional[List[str]] = None,
54
- example_suffix: Optional[str] = None,
55
- include_no_extension: bool = True,
56
- ) -> Iterator[T]:
57
- """
58
- Lazily scan directory and yield objects created by factory.
59
- """
60
-
61
- def should_include_file(filepath: str) -> bool:
62
- _, ext = os.path.splitext(filepath)
63
- ext = ext[1:] if ext else ""
64
-
65
- # Handle no extension case
66
- if not ext:
67
- return include_no_extension
68
-
69
- # Check exclusions first (they take precedence)
70
- if suffix_exclude_list and ext in suffix_exclude_list:
71
- return False
72
-
73
- # Check example suffix if specified
74
- if example_suffix and not filepath.endswith(example_suffix):
75
- return False
76
-
77
- # Check allowed suffixes if specified
78
- if suffix_allow_list and ext not in suffix_allow_list:
79
- return False
80
-
81
- return True
82
-
83
- def iter_files():
84
- if recursive:
85
- for root, _, files in os.walk(self.directory_path):
86
- for file in files:
87
- yield os.path.join(root, file)
88
- else:
89
- for file in os.listdir(self.directory_path):
90
- file_path = os.path.join(self.directory_path, file)
91
- if os.path.isfile(file_path):
92
- yield file_path
93
-
94
- for file_path in iter_files():
95
- if should_include_file(file_path):
96
- yield factory(file_path)
@@ -1,102 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Optional, Generator, TYPE_CHECKING
3
- import copy
4
-
5
- if TYPE_CHECKING:
6
- from edsl.scenarios.Scenario import Scenario
7
- from edsl.scenarios.ScenarioList import ScenarioList
8
-
9
-
10
- class DocumentChunker:
11
- def __init__(self, scenario: "Scenario"):
12
- self.scenario = scenario
13
-
14
- @staticmethod
15
- def _line_chunks(text, num_lines: int) -> Generator[str, None, None]:
16
- """Split a text into chunks of a given size.
17
-
18
- :param text: The text to split.
19
- :param num_lines: The number of lines in each chunk.
20
-
21
- Example:
22
-
23
- >>> list(DocumentChunker._line_chunks("This is a test.\\nThis is a test. This is a test.", 1))
24
- ['This is a test.', 'This is a test. This is a test.']
25
- """
26
- lines = text.split("\n")
27
- for i in range(0, len(lines), num_lines):
28
- chunk = "\n".join(lines[i : i + num_lines])
29
- yield chunk
30
-
31
- @staticmethod
32
- def _word_chunks(text, num_words: int) -> Generator[str, None, None]:
33
- """Split a text into chunks of a given size.
34
-
35
- :param text: The text to split.
36
- :param num_words: The number of words in each chunk.
37
-
38
- Example:
39
-
40
- >>> list(DocumentChunker._word_chunks("This is a test.", 2))
41
- ['This is', 'a test.']
42
- """
43
- words = text.split()
44
- for i in range(0, len(words), num_words):
45
- chunk = " ".join(words[i : i + num_words])
46
- yield chunk
47
-
48
- def chunk(
49
- self,
50
- field,
51
- num_words: Optional[int] = None,
52
- num_lines: Optional[int] = None,
53
- include_original=False,
54
- hash_original=False,
55
- ) -> ScenarioList:
56
- """Split a field into chunks of a given size.
57
-
58
- :param field: The field to split.
59
- :param num_words: The number of words in each chunk.
60
- :param num_lines: The number of lines in each chunk.
61
- :param include_original: Whether to include the original field in the new scenarios.
62
- :param hash_original: Whether to hash the original field in the new scenarios.
63
-
64
- If you specify `include_original=True`, the original field will be included in the new scenarios with an "_original" suffix.
65
- """
66
- from edsl.scenarios.ScenarioList import ScenarioList
67
- import hashlib
68
-
69
- if num_words is not None:
70
- chunks = list(self._word_chunks(self.scenario[field], num_words))
71
-
72
- if num_lines is not None:
73
- chunks = list(self._line_chunks(self.scenario[field], num_lines))
74
-
75
- if num_words is None and num_lines is None:
76
- raise ValueError("You must specify either num_words or num_lines.")
77
-
78
- if num_words is not None and num_lines is not None:
79
- raise ValueError(
80
- "You must specify either num_words or num_lines, but not both."
81
- )
82
-
83
- scenarios = []
84
- for i, chunk in enumerate(chunks):
85
- new_scenario = copy.deepcopy(self.scenario)
86
- new_scenario[field] = chunk
87
- new_scenario[field + "_chunk"] = i
88
- if include_original:
89
- if hash_original:
90
- new_scenario[field + "_original"] = hashlib.md5(
91
- self.scenario[field].encode()
92
- ).hexdigest()
93
- else:
94
- new_scenario[field + "_original"] = self.scenario[field]
95
- scenarios.append(new_scenario)
96
- return ScenarioList(scenarios)
97
-
98
-
99
- if __name__ == "__main__":
100
- import doctest
101
-
102
- doctest.testmod()