edsl 0.1.39.dev3__py3-none-any.whl → 0.1.39.dev5__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 (341) hide show
  1. edsl/Base.py +413 -332
  2. edsl/BaseDiff.py +260 -260
  3. edsl/TemplateLoader.py +24 -24
  4. edsl/__init__.py +57 -49
  5. edsl/__version__.py +1 -1
  6. edsl/agents/Agent.py +1071 -867
  7. edsl/agents/AgentList.py +551 -413
  8. edsl/agents/Invigilator.py +284 -233
  9. edsl/agents/InvigilatorBase.py +257 -270
  10. edsl/agents/PromptConstructor.py +272 -354
  11. edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
  12. edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
  13. edsl/agents/__init__.py +2 -3
  14. edsl/agents/descriptors.py +99 -99
  15. edsl/agents/prompt_helpers.py +129 -129
  16. edsl/agents/question_option_processor.py +172 -0
  17. edsl/auto/AutoStudy.py +130 -117
  18. edsl/auto/StageBase.py +243 -230
  19. edsl/auto/StageGenerateSurvey.py +178 -178
  20. edsl/auto/StageLabelQuestions.py +125 -125
  21. edsl/auto/StagePersona.py +61 -61
  22. edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
  23. edsl/auto/StagePersonaDimensionValues.py +74 -74
  24. edsl/auto/StagePersonaDimensions.py +69 -69
  25. edsl/auto/StageQuestions.py +74 -73
  26. edsl/auto/SurveyCreatorPipeline.py +21 -21
  27. edsl/auto/utilities.py +218 -224
  28. edsl/base/Base.py +279 -279
  29. edsl/config.py +177 -157
  30. edsl/conversation/Conversation.py +290 -290
  31. edsl/conversation/car_buying.py +59 -58
  32. edsl/conversation/chips.py +95 -95
  33. edsl/conversation/mug_negotiation.py +81 -81
  34. edsl/conversation/next_speaker_utilities.py +93 -93
  35. edsl/coop/CoopFunctionsMixin.py +15 -0
  36. edsl/coop/ExpectedParrotKeyHandler.py +125 -0
  37. edsl/coop/PriceFetcher.py +54 -54
  38. edsl/coop/__init__.py +2 -2
  39. edsl/coop/coop.py +1106 -1028
  40. edsl/coop/utils.py +131 -131
  41. edsl/data/Cache.py +573 -555
  42. edsl/data/CacheEntry.py +230 -233
  43. edsl/data/CacheHandler.py +168 -149
  44. edsl/data/RemoteCacheSync.py +186 -78
  45. edsl/data/SQLiteDict.py +292 -292
  46. edsl/data/__init__.py +5 -4
  47. edsl/data/orm.py +10 -10
  48. edsl/data_transfer_models.py +74 -73
  49. edsl/enums.py +202 -175
  50. edsl/exceptions/BaseException.py +21 -21
  51. edsl/exceptions/__init__.py +54 -54
  52. edsl/exceptions/agents.py +54 -42
  53. edsl/exceptions/cache.py +5 -5
  54. edsl/exceptions/configuration.py +16 -16
  55. edsl/exceptions/coop.py +10 -10
  56. edsl/exceptions/data.py +14 -14
  57. edsl/exceptions/general.py +34 -34
  58. edsl/exceptions/inference_services.py +5 -0
  59. edsl/exceptions/jobs.py +33 -33
  60. edsl/exceptions/language_models.py +63 -63
  61. edsl/exceptions/prompts.py +15 -15
  62. edsl/exceptions/questions.py +109 -91
  63. edsl/exceptions/results.py +29 -29
  64. edsl/exceptions/scenarios.py +29 -22
  65. edsl/exceptions/surveys.py +37 -37
  66. edsl/inference_services/AnthropicService.py +106 -87
  67. edsl/inference_services/AvailableModelCacheHandler.py +184 -0
  68. edsl/inference_services/AvailableModelFetcher.py +215 -0
  69. edsl/inference_services/AwsBedrock.py +118 -120
  70. edsl/inference_services/AzureAI.py +215 -217
  71. edsl/inference_services/DeepInfraService.py +18 -18
  72. edsl/inference_services/GoogleService.py +143 -148
  73. edsl/inference_services/GroqService.py +20 -20
  74. edsl/inference_services/InferenceServiceABC.py +80 -147
  75. edsl/inference_services/InferenceServicesCollection.py +138 -97
  76. edsl/inference_services/MistralAIService.py +120 -123
  77. edsl/inference_services/OllamaService.py +18 -18
  78. edsl/inference_services/OpenAIService.py +236 -224
  79. edsl/inference_services/PerplexityService.py +160 -163
  80. edsl/inference_services/ServiceAvailability.py +135 -0
  81. edsl/inference_services/TestService.py +90 -89
  82. edsl/inference_services/TogetherAIService.py +172 -170
  83. edsl/inference_services/data_structures.py +134 -0
  84. edsl/inference_services/models_available_cache.py +118 -118
  85. edsl/inference_services/rate_limits_cache.py +25 -25
  86. edsl/inference_services/registry.py +41 -41
  87. edsl/inference_services/write_available.py +10 -10
  88. edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
  89. edsl/jobs/Answers.py +43 -56
  90. edsl/jobs/FetchInvigilator.py +47 -0
  91. edsl/jobs/InterviewTaskManager.py +98 -0
  92. edsl/jobs/InterviewsConstructor.py +50 -0
  93. edsl/jobs/Jobs.py +823 -898
  94. edsl/jobs/JobsChecks.py +172 -147
  95. edsl/jobs/JobsComponentConstructor.py +189 -0
  96. edsl/jobs/JobsPrompts.py +270 -268
  97. edsl/jobs/JobsRemoteInferenceHandler.py +311 -239
  98. edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
  99. edsl/jobs/RequestTokenEstimator.py +30 -0
  100. edsl/jobs/__init__.py +1 -1
  101. edsl/jobs/async_interview_runner.py +138 -0
  102. edsl/jobs/buckets/BucketCollection.py +104 -63
  103. edsl/jobs/buckets/ModelBuckets.py +65 -65
  104. edsl/jobs/buckets/TokenBucket.py +283 -251
  105. edsl/jobs/buckets/TokenBucketAPI.py +211 -0
  106. edsl/jobs/buckets/TokenBucketClient.py +191 -0
  107. edsl/jobs/check_survey_scenario_compatibility.py +85 -0
  108. edsl/jobs/data_structures.py +120 -0
  109. edsl/jobs/decorators.py +35 -0
  110. edsl/jobs/interviews/Interview.py +396 -661
  111. edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
  112. edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
  113. edsl/jobs/interviews/InterviewStatistic.py +63 -63
  114. edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
  115. edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
  116. edsl/jobs/interviews/InterviewStatusLog.py +92 -92
  117. edsl/jobs/interviews/ReportErrors.py +66 -66
  118. edsl/jobs/interviews/interview_status_enum.py +9 -9
  119. edsl/jobs/jobs_status_enums.py +9 -0
  120. edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
  121. edsl/jobs/results_exceptions_handler.py +98 -0
  122. edsl/jobs/runners/JobsRunnerAsyncio.py +151 -466
  123. edsl/jobs/runners/JobsRunnerStatus.py +297 -330
  124. edsl/jobs/tasks/QuestionTaskCreator.py +244 -242
  125. edsl/jobs/tasks/TaskCreators.py +64 -64
  126. edsl/jobs/tasks/TaskHistory.py +470 -450
  127. edsl/jobs/tasks/TaskStatusLog.py +23 -23
  128. edsl/jobs/tasks/task_status_enum.py +161 -163
  129. edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
  130. edsl/jobs/tokens/TokenUsage.py +34 -34
  131. edsl/language_models/ComputeCost.py +63 -0
  132. edsl/language_models/LanguageModel.py +626 -668
  133. edsl/language_models/ModelList.py +164 -155
  134. edsl/language_models/PriceManager.py +127 -0
  135. edsl/language_models/RawResponseHandler.py +106 -0
  136. edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
  137. edsl/language_models/ServiceDataSources.py +0 -0
  138. edsl/language_models/__init__.py +2 -3
  139. edsl/language_models/fake_openai_call.py +15 -15
  140. edsl/language_models/fake_openai_service.py +61 -61
  141. edsl/language_models/key_management/KeyLookup.py +63 -0
  142. edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
  143. edsl/language_models/key_management/KeyLookupCollection.py +38 -0
  144. edsl/language_models/key_management/__init__.py +0 -0
  145. edsl/language_models/key_management/models.py +131 -0
  146. edsl/language_models/model.py +256 -0
  147. edsl/language_models/repair.py +156 -156
  148. edsl/language_models/utilities.py +65 -64
  149. edsl/notebooks/Notebook.py +263 -258
  150. edsl/notebooks/NotebookToLaTeX.py +142 -0
  151. edsl/notebooks/__init__.py +1 -1
  152. edsl/prompts/Prompt.py +352 -362
  153. edsl/prompts/__init__.py +2 -2
  154. edsl/questions/ExceptionExplainer.py +77 -0
  155. edsl/questions/HTMLQuestion.py +103 -0
  156. edsl/questions/QuestionBase.py +518 -664
  157. edsl/questions/QuestionBasePromptsMixin.py +221 -217
  158. edsl/questions/QuestionBudget.py +227 -227
  159. edsl/questions/QuestionCheckBox.py +359 -359
  160. edsl/questions/QuestionExtract.py +180 -182
  161. edsl/questions/QuestionFreeText.py +113 -114
  162. edsl/questions/QuestionFunctional.py +166 -166
  163. edsl/questions/QuestionList.py +223 -231
  164. edsl/questions/QuestionMatrix.py +265 -0
  165. edsl/questions/QuestionMultipleChoice.py +330 -286
  166. edsl/questions/QuestionNumerical.py +151 -153
  167. edsl/questions/QuestionRank.py +314 -324
  168. edsl/questions/Quick.py +41 -41
  169. edsl/questions/SimpleAskMixin.py +74 -73
  170. edsl/questions/__init__.py +27 -26
  171. edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +334 -289
  172. edsl/questions/compose_questions.py +98 -98
  173. edsl/questions/data_structures.py +20 -0
  174. edsl/questions/decorators.py +21 -21
  175. edsl/questions/derived/QuestionLikertFive.py +76 -76
  176. edsl/questions/derived/QuestionLinearScale.py +90 -87
  177. edsl/questions/derived/QuestionTopK.py +93 -93
  178. edsl/questions/derived/QuestionYesNo.py +82 -82
  179. edsl/questions/descriptors.py +427 -413
  180. edsl/questions/loop_processor.py +149 -0
  181. edsl/questions/prompt_templates/question_budget.jinja +13 -13
  182. edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
  183. edsl/questions/prompt_templates/question_extract.jinja +11 -11
  184. edsl/questions/prompt_templates/question_free_text.jinja +3 -3
  185. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
  186. edsl/questions/prompt_templates/question_list.jinja +17 -17
  187. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
  188. edsl/questions/prompt_templates/question_numerical.jinja +36 -36
  189. edsl/questions/{QuestionBaseGenMixin.py → question_base_gen_mixin.py} +168 -161
  190. edsl/questions/question_registry.py +177 -177
  191. edsl/questions/{RegisterQuestionsMeta.py → register_questions_meta.py} +71 -71
  192. edsl/questions/{ResponseValidatorABC.py → response_validator_abc.py} +188 -174
  193. edsl/questions/response_validator_factory.py +34 -0
  194. edsl/questions/settings.py +12 -12
  195. edsl/questions/templates/budget/answering_instructions.jinja +7 -7
  196. edsl/questions/templates/budget/question_presentation.jinja +7 -7
  197. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
  198. edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
  199. edsl/questions/templates/extract/answering_instructions.jinja +7 -7
  200. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
  201. edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
  202. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
  203. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
  204. edsl/questions/templates/list/answering_instructions.jinja +3 -3
  205. edsl/questions/templates/list/question_presentation.jinja +5 -5
  206. edsl/questions/templates/matrix/__init__.py +1 -0
  207. edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
  208. edsl/questions/templates/matrix/question_presentation.jinja +20 -0
  209. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
  210. edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
  211. edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
  212. edsl/questions/templates/numerical/question_presentation.jinja +6 -6
  213. edsl/questions/templates/rank/answering_instructions.jinja +11 -11
  214. edsl/questions/templates/rank/question_presentation.jinja +15 -15
  215. edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
  216. edsl/questions/templates/top_k/question_presentation.jinja +22 -22
  217. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
  218. edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
  219. edsl/results/CSSParameterizer.py +108 -108
  220. edsl/results/Dataset.py +587 -424
  221. edsl/results/DatasetExportMixin.py +594 -731
  222. edsl/results/DatasetTree.py +295 -275
  223. edsl/results/MarkdownToDocx.py +122 -0
  224. edsl/results/MarkdownToPDF.py +111 -0
  225. edsl/results/Result.py +557 -465
  226. edsl/results/Results.py +1183 -1165
  227. edsl/results/ResultsExportMixin.py +45 -43
  228. edsl/results/ResultsGGMixin.py +121 -121
  229. edsl/results/TableDisplay.py +125 -198
  230. edsl/results/TextEditor.py +50 -0
  231. edsl/results/__init__.py +2 -2
  232. edsl/results/file_exports.py +252 -0
  233. edsl/results/{ResultsFetchMixin.py → results_fetch_mixin.py} +33 -33
  234. edsl/results/{Selector.py → results_selector.py} +145 -135
  235. edsl/results/{ResultsToolsMixin.py → results_tools_mixin.py} +98 -98
  236. edsl/results/smart_objects.py +96 -0
  237. edsl/results/table_data_class.py +12 -0
  238. edsl/results/table_display.css +77 -77
  239. edsl/results/table_renderers.py +118 -0
  240. edsl/results/tree_explore.py +115 -115
  241. edsl/scenarios/ConstructDownloadLink.py +109 -0
  242. edsl/scenarios/DocumentChunker.py +102 -0
  243. edsl/scenarios/DocxScenario.py +16 -0
  244. edsl/scenarios/FileStore.py +511 -632
  245. edsl/scenarios/PdfExtractor.py +40 -0
  246. edsl/scenarios/Scenario.py +498 -601
  247. edsl/scenarios/ScenarioHtmlMixin.py +65 -64
  248. edsl/scenarios/ScenarioList.py +1458 -1287
  249. edsl/scenarios/ScenarioListExportMixin.py +45 -52
  250. edsl/scenarios/ScenarioListPdfMixin.py +239 -261
  251. edsl/scenarios/__init__.py +3 -4
  252. edsl/scenarios/directory_scanner.py +96 -0
  253. edsl/scenarios/file_methods.py +85 -0
  254. edsl/scenarios/handlers/__init__.py +13 -0
  255. edsl/scenarios/handlers/csv.py +38 -0
  256. edsl/scenarios/handlers/docx.py +76 -0
  257. edsl/scenarios/handlers/html.py +37 -0
  258. edsl/scenarios/handlers/json.py +111 -0
  259. edsl/scenarios/handlers/latex.py +5 -0
  260. edsl/scenarios/handlers/md.py +51 -0
  261. edsl/scenarios/handlers/pdf.py +68 -0
  262. edsl/scenarios/handlers/png.py +39 -0
  263. edsl/scenarios/handlers/pptx.py +105 -0
  264. edsl/scenarios/handlers/py.py +294 -0
  265. edsl/scenarios/handlers/sql.py +313 -0
  266. edsl/scenarios/handlers/sqlite.py +149 -0
  267. edsl/scenarios/handlers/txt.py +33 -0
  268. edsl/scenarios/{ScenarioJoin.py → scenario_join.py} +131 -127
  269. edsl/scenarios/scenario_selector.py +156 -0
  270. edsl/shared.py +1 -1
  271. edsl/study/ObjectEntry.py +173 -173
  272. edsl/study/ProofOfWork.py +113 -113
  273. edsl/study/SnapShot.py +80 -80
  274. edsl/study/Study.py +521 -528
  275. edsl/study/__init__.py +4 -4
  276. edsl/surveys/ConstructDAG.py +92 -0
  277. edsl/surveys/DAG.py +148 -148
  278. edsl/surveys/EditSurvey.py +221 -0
  279. edsl/surveys/InstructionHandler.py +100 -0
  280. edsl/surveys/Memory.py +31 -31
  281. edsl/surveys/MemoryManagement.py +72 -0
  282. edsl/surveys/MemoryPlan.py +244 -244
  283. edsl/surveys/Rule.py +327 -326
  284. edsl/surveys/RuleCollection.py +385 -387
  285. edsl/surveys/RuleManager.py +172 -0
  286. edsl/surveys/Simulator.py +75 -0
  287. edsl/surveys/Survey.py +1280 -1801
  288. edsl/surveys/SurveyCSS.py +273 -261
  289. edsl/surveys/SurveyExportMixin.py +259 -259
  290. edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +181 -179
  291. edsl/surveys/SurveyQualtricsImport.py +284 -284
  292. edsl/surveys/SurveyToApp.py +141 -0
  293. edsl/surveys/__init__.py +5 -3
  294. edsl/surveys/base.py +53 -53
  295. edsl/surveys/descriptors.py +60 -56
  296. edsl/surveys/instructions/ChangeInstruction.py +48 -49
  297. edsl/surveys/instructions/Instruction.py +56 -65
  298. edsl/surveys/instructions/InstructionCollection.py +82 -77
  299. edsl/templates/error_reporting/base.html +23 -23
  300. edsl/templates/error_reporting/exceptions_by_model.html +34 -34
  301. edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
  302. edsl/templates/error_reporting/exceptions_by_type.html +16 -16
  303. edsl/templates/error_reporting/interview_details.html +115 -115
  304. edsl/templates/error_reporting/interviews.html +19 -19
  305. edsl/templates/error_reporting/overview.html +4 -4
  306. edsl/templates/error_reporting/performance_plot.html +1 -1
  307. edsl/templates/error_reporting/report.css +73 -73
  308. edsl/templates/error_reporting/report.html +117 -117
  309. edsl/templates/error_reporting/report.js +25 -25
  310. edsl/tools/__init__.py +1 -1
  311. edsl/tools/clusters.py +192 -192
  312. edsl/tools/embeddings.py +27 -27
  313. edsl/tools/embeddings_plotting.py +118 -118
  314. edsl/tools/plotting.py +112 -112
  315. edsl/tools/summarize.py +18 -18
  316. edsl/utilities/PrettyList.py +56 -0
  317. edsl/utilities/SystemInfo.py +28 -28
  318. edsl/utilities/__init__.py +22 -22
  319. edsl/utilities/ast_utilities.py +25 -25
  320. edsl/utilities/data/Registry.py +6 -6
  321. edsl/utilities/data/__init__.py +1 -1
  322. edsl/utilities/data/scooter_results.json +1 -1
  323. edsl/utilities/decorators.py +77 -77
  324. edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
  325. edsl/utilities/interface.py +627 -627
  326. edsl/utilities/is_notebook.py +18 -0
  327. edsl/utilities/is_valid_variable_name.py +11 -0
  328. edsl/utilities/naming_utilities.py +263 -263
  329. edsl/utilities/remove_edsl_version.py +24 -0
  330. edsl/utilities/repair_functions.py +28 -28
  331. edsl/utilities/restricted_python.py +70 -70
  332. edsl/utilities/utilities.py +436 -424
  333. {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/LICENSE +21 -21
  334. {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/METADATA +13 -11
  335. edsl-0.1.39.dev5.dist-info/RECORD +358 -0
  336. {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/WHEEL +1 -1
  337. edsl/language_models/KeyLookup.py +0 -30
  338. edsl/language_models/registry.py +0 -190
  339. edsl/language_models/unused/ReplicateBase.py +0 -83
  340. edsl/results/ResultsDBMixin.py +0 -238
  341. edsl-0.1.39.dev3.dist-info/RECORD +0 -277
@@ -0,0 +1,105 @@
1
+ from edsl.scenarios.file_methods import FileMethods
2
+ import os
3
+ import tempfile
4
+
5
+
6
+ class PptxMethods(FileMethods):
7
+ suffix = "pptx"
8
+
9
+ def extract_text(self):
10
+ from pptx import Presentation
11
+
12
+ self.ppt = Presentation(self.path)
13
+
14
+ # Extract all text from slides
15
+ full_text = []
16
+ for slide in self.ppt.slides:
17
+ slide_text = []
18
+ for shape in slide.shapes:
19
+ if hasattr(shape, "text"):
20
+ slide_text.append(shape.text)
21
+ full_text.append("\n".join(slide_text))
22
+
23
+ text = "\n\n".join(full_text)
24
+ return text
25
+
26
+ def view_system(self):
27
+ import os
28
+ import subprocess
29
+
30
+ if os.path.exists(self.path):
31
+ try:
32
+ if (os_name := os.name) == "posix":
33
+ subprocess.run(["open", self.path], check=True) # macOS
34
+ elif os_name == "nt":
35
+ os.startfile(self.path) # Windows
36
+ else:
37
+ subprocess.run(["xdg-open", self.path], check=True) # Linux
38
+ except Exception as e:
39
+ print(f"Error opening PPTX: {e}")
40
+ else:
41
+ print("PPTX file was not found.")
42
+
43
+ def view_notebook(self):
44
+ from pptx import Presentation
45
+ from IPython.display import HTML, display
46
+
47
+ prs = Presentation(self.path)
48
+
49
+ # Create a simple HTML representation of the slides
50
+ html_content = []
51
+ for i, slide in enumerate(prs.slides, 1):
52
+ slide_content = []
53
+ for shape in slide.shapes:
54
+ if hasattr(shape, "text"):
55
+ slide_content.append(f"<p>{shape.text}</p>")
56
+
57
+ html_content.append(
58
+ f"""
59
+ <div style='border: 1px solid #ccc; margin: 10px; padding: 10px;'>
60
+ <h3>Slide {i}</h3>
61
+ {''.join(slide_content)}
62
+ </div>
63
+ """
64
+ )
65
+
66
+ html = f"""
67
+ <div style="width: 800px; height: 800px; padding: 20px;
68
+ border: 1px solid #ccc; overflow-y: auto;">
69
+ {''.join(html_content)}
70
+ </div>
71
+ """
72
+ display(HTML(html))
73
+
74
+ def example(self):
75
+ from pptx import Presentation
76
+ from edsl.scenarios.Scenario import Scenario
77
+ from edsl.scenarios.ScenarioList import ScenarioList
78
+
79
+ os.makedirs("test_dir", exist_ok=True)
80
+
81
+ # Create first presentation
82
+ ppt1 = Presentation()
83
+ slide = ppt1.slides.add_slide(ppt1.slide_layouts[0])
84
+ title = slide.shapes.title
85
+ title.text = "First Presentation"
86
+ ppt1.save("test_dir/test1.pptx")
87
+
88
+ # Create second presentation
89
+ ppt2 = Presentation()
90
+ slide = ppt2.slides.add_slide(ppt2.slide_layouts[0])
91
+ title = slide.shapes.title
92
+ title.text = "Second Presentation"
93
+
94
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pptx") as tmp:
95
+ ppt2.save(tmp.name)
96
+ tmp.close()
97
+
98
+ return tmp.name
99
+
100
+
101
+ if __name__ == "__main__":
102
+ pptx_temp = PptxMethods.example()
103
+ from edsl.scenarios.FileStore import FileStore
104
+
105
+ fs = FileStore(pptx_temp)
@@ -0,0 +1,294 @@
1
+ from edsl.scenarios.file_methods import FileMethods
2
+ import tempfile
3
+ import re
4
+ from typing import List, Optional, Dict
5
+ import ast
6
+ import black
7
+ import textwrap
8
+ import subprocess
9
+ import sys
10
+ from importlib import util
11
+
12
+
13
+ class PyMethods(FileMethods):
14
+ suffix = "py"
15
+
16
+ def view_system(self):
17
+ """Open the Python file in the system's default editor."""
18
+ import os
19
+ import subprocess
20
+
21
+ if os.path.exists(self.path):
22
+ try:
23
+ if (os_name := os.name) == "posix":
24
+ subprocess.run(["open", self.path], check=True) # macOS
25
+ elif os_name == "nt":
26
+ os.startfile(self.path) # Windows
27
+ else:
28
+ subprocess.run(["xdg-open", self.path], check=True) # Linux
29
+ except Exception as e:
30
+ print(f"Error opening Python file: {e}")
31
+ else:
32
+ print("Python file was not found.")
33
+
34
+ def view_notebook(self):
35
+ """Display the Python file with syntax highlighting in a notebook."""
36
+ from IPython.display import FileLink, display, HTML
37
+ import pygments
38
+ from pygments.lexers import PythonLexer
39
+ from pygments.formatters import HtmlFormatter
40
+ from pygments.styles import get_style_by_name
41
+
42
+ try:
43
+ with open(self.path, "r", encoding="utf-8") as f:
44
+ content = f.read()
45
+
46
+ # Create custom CSS for better visibility in both light and dark themes
47
+ custom_css = """
48
+ .highlight {
49
+ background: var(--jp-cell-editor-background, #f7f7f7);
50
+ border: 1px solid var(--jp-border-color2, #ddd);
51
+ border-radius: 3px;
52
+ padding: 1em;
53
+ margin: 1em 0;
54
+ }
55
+ .highlight pre {
56
+ margin: 0;
57
+ color: var(--jp-content-font-color0, #000);
58
+ background: transparent;
59
+ }
60
+ .highlight .hll { background-color: var(--jp-cell-editor-active-background, #ffffcc) }
61
+ .highlight .c { color: #408080; font-style: italic } /* Comment */
62
+ .highlight .k { color: #008000; font-weight: bold } /* Keyword */
63
+ .highlight .o { color: #666666 } /* Operator */
64
+ .highlight .s { color: #BA2121 } /* String */
65
+ .highlight .n { color: var(--jp-content-font-color0, #000) } /* Name */
66
+ .highlight .p { color: var(--jp-content-font-color0, #000) } /* Punctuation */
67
+ """
68
+
69
+ formatter = HtmlFormatter(style="default")
70
+ highlighted_python = pygments.highlight(content, PythonLexer(), formatter)
71
+
72
+ # Combine the custom CSS with basic formatter CSS
73
+ css = formatter.get_style_defs(".highlight") + custom_css
74
+
75
+ display(HTML(f"<style>{css}</style>{highlighted_python}"))
76
+ display(FileLink(self.path))
77
+ except Exception as e:
78
+ print(f"Error displaying Python: {e}")
79
+
80
+ def format_python(self) -> bool:
81
+ """Format the Python file using black."""
82
+ try:
83
+ with open(self.path, "r", encoding="utf-8") as f:
84
+ content = f.read()
85
+
86
+ # Format using black
87
+ formatted_content = black.format_str(content, mode=black.FileMode())
88
+
89
+ with open(self.path, "w", encoding="utf-8") as f:
90
+ f.write(formatted_content)
91
+
92
+ return True
93
+ except Exception as e:
94
+ print(f"Error formatting Python: {e}")
95
+ return False
96
+
97
+ def validate_syntax(self) -> bool:
98
+ """Validate Python syntax using ast.parse."""
99
+ try:
100
+ with open(self.path, "r", encoding="utf-8") as f:
101
+ content = f.read()
102
+
103
+ ast.parse(content)
104
+ return True
105
+ except SyntaxError as e:
106
+ print(f"Syntax error in Python file: {e}")
107
+ return False
108
+ except Exception as e:
109
+ print(f"Error validating Python: {e}")
110
+ return False
111
+
112
+ def extract_imports(self) -> List[str]:
113
+ """Extract all import statements from the Python file."""
114
+ imports = []
115
+ try:
116
+ with open(self.path, "r", encoding="utf-8") as f:
117
+ content = f.read()
118
+
119
+ tree = ast.parse(content)
120
+ for node in ast.walk(tree):
121
+ if isinstance(node, ast.Import):
122
+ for name in node.names:
123
+ imports.append(name.name)
124
+ elif isinstance(node, ast.ImportFrom):
125
+ module = node.module or ""
126
+ for name in node.names:
127
+ imports.append(f"{module}.{name.name}")
128
+
129
+ return sorted(list(set(imports)))
130
+ except Exception as e:
131
+ print(f"Error extracting imports: {e}")
132
+ return []
133
+
134
+ def extract_functions(self) -> List[str]:
135
+ """Extract all function names from the Python file."""
136
+ functions = []
137
+ try:
138
+ with open(self.path, "r", encoding="utf-8") as f:
139
+ content = f.read()
140
+
141
+ tree = ast.parse(content)
142
+ for node in ast.walk(tree):
143
+ if isinstance(node, ast.FunctionDef):
144
+ functions.append(node.name)
145
+
146
+ return sorted(functions)
147
+ except Exception as e:
148
+ print(f"Error extracting functions: {e}")
149
+ return []
150
+
151
+ def extract_classes(self) -> List[str]:
152
+ """Extract all class names from the Python file."""
153
+ classes = []
154
+ try:
155
+ with open(self.path, "r", encoding="utf-8") as f:
156
+ content = f.read()
157
+
158
+ tree = ast.parse(content)
159
+ for node in ast.walk(tree):
160
+ if isinstance(node, ast.ClassDef):
161
+ classes.append(node.name)
162
+
163
+ return sorted(classes)
164
+ except Exception as e:
165
+ print(f"Error extracting classes: {e}")
166
+ return []
167
+
168
+ def get_docstrings(self) -> Dict[str, str]:
169
+ """Extract docstrings for all functions and classes."""
170
+ docstrings = {}
171
+ try:
172
+ with open(self.path, "r", encoding="utf-8") as f:
173
+ content = f.read()
174
+
175
+ tree = ast.parse(content)
176
+ for node in ast.walk(tree):
177
+ if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
178
+ docstring = ast.get_docstring(node)
179
+ if docstring:
180
+ docstrings[node.name] = docstring
181
+
182
+ return docstrings
183
+ except Exception as e:
184
+ print(f"Error extracting docstrings: {e}")
185
+ return {}
186
+
187
+ def run_file(self, args: List[str] = None) -> Optional[int]:
188
+ """Run the Python file as a script with optional arguments."""
189
+ try:
190
+ cmd = [sys.executable, self.path]
191
+ if args:
192
+ cmd.extend(args)
193
+
194
+ result = subprocess.run(cmd, capture_output=True, text=True)
195
+ print(result.stdout)
196
+ if result.stderr:
197
+ print("Errors:", result.stderr, file=sys.stderr)
198
+ return result.returncode
199
+ except Exception as e:
200
+ print(f"Error running Python file: {e}")
201
+ return None
202
+
203
+ def check_dependencies(self) -> List[str]:
204
+ """Check if all imported modules are available."""
205
+ missing_deps = []
206
+ try:
207
+ imports = self.extract_imports()
208
+ for imp in imports:
209
+ # Get the top-level module name
210
+ top_module = imp.split(".")[0]
211
+ if not util.find_spec(top_module):
212
+ missing_deps.append(top_module)
213
+ return missing_deps
214
+ except Exception as e:
215
+ print(f"Error checking dependencies: {e}")
216
+ return []
217
+
218
+ def example(self):
219
+ """Create a sample Python file with common patterns."""
220
+ sample_python = '''#!/usr/bin/env python3
221
+ """Example Python module demonstrating common patterns."""
222
+
223
+ import sys
224
+ from typing import List, Optional
225
+ from dataclasses import dataclass
226
+
227
+
228
+ @dataclass
229
+ class Employee:
230
+ """Represents an employee in the system."""
231
+ name: str
232
+ department: str
233
+ salary: float
234
+ hire_date: str
235
+
236
+
237
+ class EmployeeManager:
238
+ """Manages employee operations."""
239
+
240
+ def __init__(self):
241
+ self.employees: List[Employee] = []
242
+
243
+ def add_employee(self, employee: Employee) -> None:
244
+ """Add a new employee to the system."""
245
+ self.employees.append(employee)
246
+
247
+ def get_department_stats(self, department: str) -> Optional[dict]:
248
+ """Calculate statistics for a department."""
249
+ dept_employees = [e for e in self.employees if e.department == department]
250
+
251
+ if not dept_employees:
252
+ return None
253
+
254
+ return {
255
+ 'count': len(dept_employees),
256
+ 'avg_salary': sum(e.salary for e in dept_employees) / len(dept_employees)
257
+ }
258
+
259
+
260
+ def main(args: List[str]) -> int:
261
+ """Main entry point for the script."""
262
+ manager = EmployeeManager()
263
+
264
+ # Add sample employees
265
+ manager.add_employee(Employee(
266
+ "John Doe",
267
+ "Engineering",
268
+ 75000.00,
269
+ "2023-01-15"
270
+ ))
271
+
272
+ manager.add_employee(Employee(
273
+ "Jane Smith",
274
+ "Marketing",
275
+ 65000.00,
276
+ "2023-02-01"
277
+ ))
278
+
279
+ # Print department statistics
280
+ stats = manager.get_department_stats("Engineering")
281
+ if stats:
282
+ print(f"Engineering department stats: {stats}")
283
+
284
+ return 0
285
+
286
+
287
+ if __name__ == "__main__":
288
+ sys.exit(main(sys.argv[1:]))
289
+ '''
290
+ with tempfile.NamedTemporaryFile(
291
+ delete=False, suffix=".py", mode="w", encoding="utf-8"
292
+ ) as f:
293
+ f.write(sample_python)
294
+ return f.name
@@ -0,0 +1,313 @@
1
+ from edsl.scenarios.file_methods import FileMethods
2
+ import tempfile
3
+ import re
4
+ from typing import List, Optional
5
+ import textwrap
6
+
7
+
8
+ class SqlMethods(FileMethods):
9
+ suffix = "sql"
10
+
11
+ def view_system(self):
12
+ import os
13
+ import subprocess
14
+
15
+ if os.path.exists(self.path):
16
+ try:
17
+ if (os_name := os.name) == "posix":
18
+ subprocess.run(["open", self.path], check=True) # macOS
19
+ elif os_name == "nt":
20
+ os.startfile(self.path) # Windows
21
+ else:
22
+ subprocess.run(["xdg-open", self.path], check=True) # Linux
23
+ except Exception as e:
24
+ print(f"Error opening SQL file: {e}")
25
+ else:
26
+ print("SQL file was not found.")
27
+
28
+ def view_notebook(self):
29
+ from IPython.display import FileLink, display, HTML
30
+ import pygments
31
+ from pygments.lexers import SqlLexer
32
+ from pygments.formatters import HtmlFormatter
33
+
34
+ try:
35
+ with open(self.path, "r", encoding="utf-8") as f:
36
+ content = f.read()
37
+
38
+ formatter = HtmlFormatter(style="monokai")
39
+ highlighted_sql = pygments.highlight(content, SqlLexer(), formatter)
40
+ css = formatter.get_style_defs(".highlight")
41
+ display(HTML(f"<style>{css}</style>{highlighted_sql}"))
42
+ display(FileLink(self.path))
43
+ except Exception as e:
44
+ print(f"Error displaying SQL: {e}")
45
+
46
+ def _format_keywords(self, sql: str) -> str:
47
+ """Capitalize SQL keywords."""
48
+ keywords = {
49
+ "select",
50
+ "from",
51
+ "where",
52
+ "and",
53
+ "or",
54
+ "insert",
55
+ "update",
56
+ "delete",
57
+ "create",
58
+ "drop",
59
+ "alter",
60
+ "table",
61
+ "into",
62
+ "values",
63
+ "group",
64
+ "by",
65
+ "having",
66
+ "order",
67
+ "limit",
68
+ "join",
69
+ "left",
70
+ "right",
71
+ "inner",
72
+ "outer",
73
+ "on",
74
+ "as",
75
+ "distinct",
76
+ "count",
77
+ "sum",
78
+ "avg",
79
+ "max",
80
+ "min",
81
+ "between",
82
+ "like",
83
+ "in",
84
+ "is",
85
+ "null",
86
+ "not",
87
+ "case",
88
+ "when",
89
+ "then",
90
+ "else",
91
+ "end",
92
+ }
93
+
94
+ words = sql.split()
95
+ formatted_words = []
96
+ for word in words:
97
+ lower_word = word.lower()
98
+ if lower_word in keywords:
99
+ formatted_words.append(word.upper())
100
+ else:
101
+ formatted_words.append(word.lower())
102
+ return " ".join(formatted_words)
103
+
104
+ def _indent_sql(self, sql: str) -> str:
105
+ """Add basic indentation to SQL statement."""
106
+ lines = sql.split("\n")
107
+ indented_lines = []
108
+ indent_level = 0
109
+
110
+ for line in lines:
111
+ line = line.strip()
112
+
113
+ # Decrease indent for closing parentheses
114
+ if line.startswith(")"):
115
+ indent_level = max(0, indent_level - 1)
116
+
117
+ # Add indentation
118
+ if line:
119
+ indented_lines.append(" " * indent_level + line)
120
+ else:
121
+ indented_lines.append("")
122
+
123
+ # Increase indent after opening parentheses
124
+ if line.endswith("("):
125
+ indent_level += 1
126
+
127
+ # Special cases for common SQL clauses
128
+ lower_line = line.lower()
129
+ if any(
130
+ clause in lower_line
131
+ for clause in [
132
+ "select",
133
+ "from",
134
+ "where",
135
+ "group by",
136
+ "having",
137
+ "order by",
138
+ ]
139
+ ):
140
+ indent_level = 1
141
+
142
+ return "\n".join(indented_lines)
143
+
144
+ def format_sql(self) -> bool:
145
+ """Format the SQL file with proper indentation and keyword capitalization."""
146
+ try:
147
+ with open(self.path, "r", encoding="utf-8") as f:
148
+ content = f.read()
149
+
150
+ # Remove extra whitespace and format
151
+ content = " ".join(content.split())
152
+ content = self._format_keywords(content)
153
+ content = self._indent_sql(content)
154
+
155
+ # Wrap long lines
156
+ wrapped_content = []
157
+ for line in content.split("\n"):
158
+ if len(line) > 80:
159
+ wrapped_line = textwrap.fill(
160
+ line, width=80, subsequent_indent=" "
161
+ )
162
+ wrapped_content.append(wrapped_line)
163
+ else:
164
+ wrapped_content.append(line)
165
+
166
+ formatted_sql = "\n".join(wrapped_content)
167
+
168
+ with open(self.path, "w", encoding="utf-8") as f:
169
+ f.write(formatted_sql)
170
+
171
+ return True
172
+ except Exception as e:
173
+ print(f"Error formatting SQL: {e}")
174
+ return False
175
+
176
+ def split_statements(self) -> List[str]:
177
+ """Split the SQL file into individual statements."""
178
+ try:
179
+ with open(self.path, "r", encoding="utf-8") as f:
180
+ content = f.read()
181
+
182
+ # Handle both semicolon and GO statement terminators
183
+ statements = []
184
+ current_stmt = []
185
+
186
+ for line in content.split("\n"):
187
+ line = line.strip()
188
+
189
+ # Skip empty lines and comments
190
+ if not line or line.startswith("--"):
191
+ continue
192
+
193
+ if line.endswith(";"):
194
+ current_stmt.append(line[:-1]) # Remove semicolon
195
+ statements.append(" ".join(current_stmt))
196
+ current_stmt = []
197
+ elif line.upper() == "GO":
198
+ if current_stmt:
199
+ statements.append(" ".join(current_stmt))
200
+ current_stmt = []
201
+ else:
202
+ current_stmt.append(line)
203
+
204
+ # Add any remaining statement
205
+ if current_stmt:
206
+ statements.append(" ".join(current_stmt))
207
+
208
+ return [stmt.strip() for stmt in statements if stmt.strip()]
209
+ except Exception as e:
210
+ print(f"Error splitting SQL statements: {e}")
211
+ return []
212
+
213
+ def validate_basic_syntax(self) -> bool:
214
+ """
215
+ Perform basic SQL syntax validation.
216
+ This is a simple check and doesn't replace proper SQL parsing.
217
+ """
218
+ try:
219
+ with open(self.path, "r", encoding="utf-8") as f:
220
+ content = f.read()
221
+
222
+ statements = self.split_statements()
223
+ for stmt in statements:
224
+ # Check for basic SQL keywords
225
+ stmt_upper = stmt.upper()
226
+ if not any(
227
+ keyword in stmt_upper
228
+ for keyword in [
229
+ "SELECT",
230
+ "INSERT",
231
+ "UPDATE",
232
+ "DELETE",
233
+ "CREATE",
234
+ "DROP",
235
+ "ALTER",
236
+ ]
237
+ ):
238
+ print(f"Warning: Statement might be incomplete: {stmt}")
239
+
240
+ # Check for basic parentheses matching
241
+ if stmt.count("(") != stmt.count(")"):
242
+ print(f"Error: Unmatched parentheses in statement: {stmt}")
243
+ return False
244
+
245
+ # Check for basic quote matching
246
+ if stmt.count("'") % 2 != 0:
247
+ print(f"Error: Unmatched quotes in statement: {stmt}")
248
+ return False
249
+
250
+ return True
251
+ except Exception as e:
252
+ print(f"Error validating SQL: {e}")
253
+ return False
254
+
255
+ def extract_table_names(self) -> List[str]:
256
+ """Extract table names from the SQL file."""
257
+ tables = set()
258
+ try:
259
+ with open(self.path, "r", encoding="utf-8") as f:
260
+ content = f.read()
261
+
262
+ patterns = [
263
+ r"FROM\s+([a-zA-Z_][a-zA-Z0-9_]*)",
264
+ r"JOIN\s+([a-zA-Z_][a-zA-Z0-9_]*)",
265
+ r"UPDATE\s+([a-zA-Z_][a-zA-Z0-9_]*)",
266
+ r"INSERT\s+INTO\s+([a-zA-Z_][a-zA-Z0-9_]*)",
267
+ r"CREATE\s+TABLE\s+([a-zA-Z_][a-zA-Z0-9_]*)",
268
+ ]
269
+
270
+ for pattern in patterns:
271
+ tables.update(re.findall(pattern, content, re.IGNORECASE))
272
+
273
+ return sorted(list(tables))
274
+ except Exception as e:
275
+ print(f"Error extracting table names: {e}")
276
+ return []
277
+
278
+ def example(self):
279
+ sample_sql = """-- Sample SQL file with common operations
280
+ CREATE TABLE employees (
281
+ id INTEGER PRIMARY KEY,
282
+ name VARCHAR(100) NOT NULL,
283
+ department VARCHAR(50),
284
+ salary DECIMAL(10,2),
285
+ hire_date DATE
286
+ );
287
+
288
+ INSERT INTO employees (name, department, salary, hire_date)
289
+ VALUES
290
+ ('John Doe', 'Engineering', 75000.00, '2023-01-15'),
291
+ ('Jane Smith', 'Marketing', 65000.00, '2023-02-01');
292
+
293
+ -- Query to analyze employee data
294
+ SELECT
295
+ department,
296
+ COUNT(*) as employee_count,
297
+ AVG(salary) as avg_salary
298
+ FROM employees
299
+ GROUP BY department
300
+ HAVING COUNT(*) > 0
301
+ ORDER BY avg_salary DESC;
302
+
303
+ -- Update salary with conditions
304
+ UPDATE employees
305
+ SET salary = salary * 1.1
306
+ WHERE department = 'Engineering'
307
+ AND hire_date < '2024-01-01';
308
+ """
309
+ with tempfile.NamedTemporaryFile(
310
+ delete=False, suffix=".sql", mode="w", encoding="utf-8"
311
+ ) as f:
312
+ f.write(sample_sql)
313
+ return f.name