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,103 +0,0 @@
1
- from typing import Optional
2
- from edsl.prompts.Prompt import Prompt
3
-
4
-
5
- class HTMLQuestion:
6
- def __init__(self, question):
7
- self.question = question
8
-
9
- def html(
10
- self,
11
- scenario: Optional[dict] = None,
12
- agent: Optional[dict] = {},
13
- answers: Optional[dict] = None,
14
- include_question_name: bool = False,
15
- height: Optional[int] = None,
16
- width: Optional[int] = None,
17
- iframe=False,
18
- ):
19
- """Return the question in HTML format."""
20
- from jinja2 import Template
21
-
22
- if scenario is None:
23
- scenario = {}
24
-
25
- prior_answers_dict = {}
26
-
27
- if isinstance(answers, dict):
28
- for key, value in answers.items():
29
- if not key.endswith("_comment") and not key.endswith(
30
- "_generated_tokens"
31
- ):
32
- prior_answers_dict[key] = {"answer": value}
33
-
34
- base_template = """
35
- <div id="{{ question_name }}" class="survey_question" data-type="{{ question_type }}">
36
- {% if include_question_name %}
37
- <p>question_name: {{ question_name }}</p>
38
- {% endif %}
39
- <p class="question_text">{{ question_text }}</p>
40
- {{ question_content }}
41
- </div>
42
- """
43
- if not hasattr(self.question, "question_type"):
44
- self.question.question_type = "unknown"
45
-
46
- if hasattr(self.question, "question_html_content"):
47
- question_content = self.question.question_html_content
48
- else:
49
- question_content = Template("")
50
-
51
- base_template = Template(base_template)
52
-
53
- context = {
54
- "scenario": scenario,
55
- "agent": agent,
56
- } | prior_answers_dict
57
-
58
- # Render the question text
59
- try:
60
- question_text = Template(self.question.question_text).render(context)
61
- except Exception as e:
62
- print(
63
- f"Error rendering question: question_text = {self.question.question_text}, error = {e}"
64
- )
65
- question_text = self.question.question_text
66
-
67
- try:
68
- question_content = Template(question_content).render(context)
69
- except Exception as e:
70
- print(
71
- f"Error rendering question: question_content = {question_content}, error = {e}"
72
- )
73
- question_content = question_content
74
-
75
- try:
76
- params = {
77
- "question_name": self.question.question_name,
78
- "question_text": question_text,
79
- "question_type": self.question.question_type,
80
- "question_content": question_content,
81
- "include_question_name": include_question_name,
82
- }
83
- except Exception as e:
84
- raise ValueError(
85
- f"Error rendering question: params = {params}, error = {e}"
86
- )
87
- rendered_html = base_template.render(**params)
88
-
89
- if iframe:
90
- import html
91
- from IPython.display import display, HTML
92
-
93
- height = height or 200
94
- width = width or 600
95
- escaped_output = html.escape(rendered_html)
96
- # escaped_output = rendered_html
97
- iframe = f""""
98
- <iframe srcdoc="{ escaped_output }" style="width: {width}px; height: {height}px;"></iframe>
99
- """
100
- display(HTML(iframe))
101
- return None
102
-
103
- return rendered_html
@@ -1,149 +0,0 @@
1
- from typing import List, Any, Dict, Union
2
- from jinja2 import Environment
3
- from edsl.questions.QuestionBase import QuestionBase
4
- from edsl import ScenarioList
5
-
6
-
7
- class LoopProcessor:
8
- def __init__(self, question: QuestionBase):
9
- self.question = question
10
- self.env = Environment()
11
-
12
- def process_templates(self, scenario_list: ScenarioList) -> List[QuestionBase]:
13
- """Process templates for each scenario and return list of modified questions.
14
-
15
- Args:
16
- scenario_list: List of scenarios to process templates against
17
-
18
- Returns:
19
- List of QuestionBase objects with rendered templates
20
- """
21
- questions = []
22
- starting_name = self.question.question_name
23
-
24
- for index, scenario in enumerate(scenario_list):
25
- question_data = self.question.to_dict().copy()
26
- processed_data = self._process_data(question_data, scenario)
27
-
28
- if processed_data["question_name"] == starting_name:
29
- processed_data["question_name"] += f"_{index}"
30
-
31
- questions.append(QuestionBase.from_dict(processed_data))
32
-
33
- return questions
34
-
35
- def _process_data(
36
- self, data: Dict[str, Any], scenario: Dict[str, Any]
37
- ) -> Dict[str, Any]:
38
- """Process all data fields according to their type.
39
-
40
- Args:
41
- data: Dictionary of question data
42
- scenario: Current scenario to render templates against
43
-
44
- Returns:
45
- Processed dictionary with rendered templates
46
- """
47
- processed = {}
48
-
49
- for key, value in [(k, v) for k, v in data.items() if v is not None]:
50
- processed[key] = self._process_value(key, value, scenario)
51
-
52
- return processed
53
-
54
- def _process_value(self, key: str, value: Any, scenario: Dict[str, Any]) -> Any:
55
- """Process a single value according to its type.
56
-
57
- Args:
58
- key: Dictionary key
59
- value: Value to process
60
- scenario: Current scenario
61
-
62
- Returns:
63
- Processed value
64
- """
65
- if key == "question_options" and isinstance(value, str):
66
- return value
67
-
68
- if key == "option_labels":
69
- import json
70
-
71
- return (
72
- eval(self._render_template(value, scenario))
73
- if isinstance(value, str)
74
- else value
75
- )
76
-
77
- if isinstance(value, str):
78
- return self._render_template(value, scenario)
79
-
80
- if isinstance(value, list):
81
- return self._process_list(value, scenario)
82
-
83
- if isinstance(value, dict):
84
- return self._process_dict(value, scenario)
85
-
86
- if isinstance(value, (int, float)):
87
- return value
88
-
89
- raise ValueError(f"Unexpected value type: {type(value)} for key '{key}'")
90
-
91
- def _render_template(self, template: str, scenario: Dict[str, Any]) -> str:
92
- """Render a single template string.
93
-
94
- Args:
95
- template: Template string to render
96
- scenario: Current scenario
97
-
98
- Returns:
99
- Rendered template string
100
- """
101
- return self.env.from_string(template).render(scenario)
102
-
103
- def _process_list(self, items: List[Any], scenario: Dict[str, Any]) -> List[Any]:
104
- """Process all items in a list.
105
-
106
- Args:
107
- items: List of items to process
108
- scenario: Current scenario
109
-
110
- Returns:
111
- List of processed items
112
- """
113
- return [
114
- self._render_template(item, scenario) if isinstance(item, str) else item
115
- for item in items
116
- ]
117
-
118
- def _process_dict(
119
- self, data: Dict[str, Any], scenario: Dict[str, Any]
120
- ) -> Dict[str, Any]:
121
- """Process all keys and values in a dictionary.
122
-
123
- Args:
124
- data: Dictionary to process
125
- scenario: Current scenario
126
-
127
- Returns:
128
- Dictionary with processed keys and values
129
- """
130
- return {
131
- (self._render_template(k, scenario) if isinstance(k, str) else k): (
132
- self._render_template(v, scenario) if isinstance(v, str) else v
133
- )
134
- for k, v in data.items()
135
- }
136
-
137
-
138
- # Usage example:
139
- """
140
- from edsl import QuestionFreeText, ScenarioList
141
-
142
- question = QuestionFreeText(
143
- question_text="What are your thoughts on: {{subject}}?",
144
- question_name="base_{{subject}}"
145
- )
146
- processor = TemplateProcessor(question)
147
- scenarios = ScenarioList.from_list("subject", ["Math", "Economics", "Chemistry"])
148
- processed_questions = processor.process_templates(scenarios)
149
- """
@@ -1,265 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Union, Optional, Dict, List, Any
3
-
4
- from pydantic import BaseModel, Field, field_validator
5
- from jinja2 import Template
6
- import random
7
- from edsl.questions.QuestionBase import QuestionBase
8
- from edsl.questions.descriptors import (
9
- QuestionOptionsDescriptor,
10
- OptionLabelDescriptor,
11
- QuestionTextDescriptor,
12
- )
13
- from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
14
- from edsl.questions.decorators import inject_exception
15
- from edsl.exceptions.questions import (
16
- QuestionAnswerValidationError,
17
- QuestionCreationValidationError,
18
- )
19
-
20
-
21
- def create_matrix_response(
22
- question_items: List[str],
23
- question_options: List[Union[int, str, float]],
24
- permissive: bool = False,
25
- ):
26
- """Create a response model for matrix questions.
27
-
28
- The response model validates that:
29
- 1. All question items are answered
30
- 2. Each answer is from the allowed options
31
- """
32
-
33
- if permissive:
34
-
35
- class MatrixResponse(BaseModel):
36
- answer: Dict[str, Any]
37
- comment: Optional[str] = None
38
- generated_tokens: Optional[Any] = None
39
-
40
- else:
41
-
42
- class MatrixResponse(BaseModel):
43
- answer: Dict[str, Union[int, str, float]] = Field(
44
- ..., description="Mapping of items to selected options"
45
- )
46
- comment: Optional[str] = None
47
- generated_tokens: Optional[Any] = None
48
-
49
- @field_validator("answer")
50
- def validate_answer(cls, v, values, **kwargs):
51
- # Check that all items have responses
52
- if not all(item in v for item in question_items):
53
- missing = set(question_items) - set(v.keys())
54
- raise ValueError(f"Missing responses for items: {missing}")
55
-
56
- # Check that all responses are valid options
57
- if not all(answer in question_options for answer in v.values()):
58
- invalid = [ans for ans in v.values() if ans not in question_options]
59
- raise ValueError(f"Invalid options selected: {invalid}")
60
- return v
61
-
62
- return MatrixResponse
63
-
64
-
65
- class MatrixResponseValidator(ResponseValidatorABC):
66
- required_params = ["question_items", "question_options", "permissive"]
67
-
68
- valid_examples = [
69
- (
70
- {"answer": {"Item1": 1, "Item2": 2}},
71
- {
72
- "question_items": ["Item1", "Item2"],
73
- "question_options": [1, 2, 3],
74
- },
75
- )
76
- ]
77
-
78
- invalid_examples = [
79
- (
80
- {"answer": {"Item1": 1}},
81
- {
82
- "question_items": ["Item1", "Item2"],
83
- "question_options": [1, 2, 3],
84
- },
85
- "Missing responses for some items",
86
- ),
87
- (
88
- {"answer": {"Item1": 4, "Item2": 5}},
89
- {
90
- "question_items": ["Item1", "Item2"],
91
- "question_options": [1, 2, 3],
92
- },
93
- "Invalid options selected",
94
- ),
95
- ]
96
-
97
- def fix(self, response, verbose=False):
98
- if verbose:
99
- print(f"Fixing matrix response: {response}")
100
-
101
- # If we have generated tokens, try to parse them
102
- if "generated_tokens" in response:
103
- try:
104
- import json
105
-
106
- fixed = json.loads(response["generated_tokens"])
107
- if isinstance(fixed, dict):
108
- # Map numeric keys to question items
109
- mapped_answer = {}
110
- for idx, item in enumerate(self.question_items):
111
- if str(idx) in fixed:
112
- mapped_answer[item] = fixed[str(idx)]
113
- if (
114
- mapped_answer
115
- ): # Only return if we successfully mapped some answers
116
- return {"answer": mapped_answer}
117
- except:
118
- pass
119
-
120
- # If answer uses numeric keys, map them to question items
121
- if "answer" in response and isinstance(response["answer"], dict):
122
- if all(str(key).isdigit() for key in response["answer"].keys()):
123
- mapped_answer = {}
124
- for idx, item in enumerate(self.question_items):
125
- if str(idx) in response["answer"]:
126
- mapped_answer[item] = response["answer"][str(idx)]
127
- if mapped_answer: # Only update if we successfully mapped some answers
128
- response["answer"] = mapped_answer
129
-
130
- return response
131
-
132
-
133
- class QuestionMatrix(QuestionBase):
134
- """A question that presents a matrix/grid where multiple items are rated using the same scale."""
135
-
136
- question_type = "matrix"
137
- question_text: str = QuestionTextDescriptor()
138
- question_items: List[str] = QuestionOptionsDescriptor()
139
- question_options: List[Union[int, str, float]] = QuestionOptionsDescriptor()
140
- option_labels: Optional[Dict[Union[int, str, float], str]] = OptionLabelDescriptor()
141
-
142
- _response_model = None
143
- response_validator_class = MatrixResponseValidator
144
-
145
- def __init__(
146
- self,
147
- question_name: str,
148
- question_text: str,
149
- question_items: List[str],
150
- question_options: List[Union[int, str, float]],
151
- option_labels: Optional[Dict[Union[int, str, float], str]] = None,
152
- include_comment: bool = True,
153
- answering_instructions: Optional[str] = None,
154
- question_presentation: Optional[str] = None,
155
- permissive: bool = False,
156
- ):
157
- """Initialize a matrix question.
158
-
159
- Args:
160
- question_name: The name of the question
161
- question_text: The text of the question
162
- question_items: List of items to be rated
163
- question_options: List of rating options
164
- option_labels: Optional mapping of options to their labels
165
- include_comment: Whether to include a comment field
166
- answering_instructions: Optional custom instructions
167
- question_presentation: Optional custom presentation
168
- permissive: Whether to strictly validate responses
169
- """
170
- self.question_name = question_name
171
-
172
- try:
173
- self.question_text = question_text
174
- except Exception as e:
175
- raise QuestionCreationValidationError(
176
- "question_text cannot be empty or too short!"
177
- ) from e
178
-
179
- self.question_items = question_items
180
- self.question_options = question_options
181
- self.option_labels = option_labels or {}
182
-
183
- self.include_comment = include_comment
184
- self.answering_instructions = answering_instructions
185
- self.question_presentation = question_presentation
186
- self.permissive = permissive
187
-
188
- def create_response_model(self):
189
- return create_matrix_response(
190
- self.question_items, self.question_options, self.permissive
191
- )
192
-
193
- @property
194
- def question_html_content(self) -> str:
195
- """Generate HTML representation of the matrix question."""
196
- template = Template(
197
- """
198
- <table class="matrix-question">
199
- <tr>
200
- <th></th>
201
- {% for option in question_options %}
202
- <th>
203
- {{ option }}
204
- {% if option in option_labels %}
205
- <br>
206
- <small>{{ option_labels[option] }}</small>
207
- {% endif %}
208
- </th>
209
- {% endfor %}
210
- </tr>
211
- {% for item in question_items %}
212
- <tr>
213
- <td>{{ item }}</td>
214
- {% for option in question_options %}
215
- <td>
216
- <input type="radio"
217
- name="{{ question_name }}_{{ item }}"
218
- value="{{ option }}"
219
- id="{{ question_name }}_{{ item }}_{{ option }}">
220
- </td>
221
- {% endfor %}
222
- </tr>
223
- {% endfor %}
224
- </table>
225
- """
226
- )
227
-
228
- return template.render(
229
- question_name=self.question_name,
230
- question_items=self.question_items,
231
- question_options=self.question_options,
232
- option_labels=self.option_labels,
233
- )
234
-
235
- @classmethod
236
- @inject_exception
237
- def example(cls) -> QuestionMatrix:
238
- """Return an example matrix question."""
239
- return cls(
240
- question_name="child_happiness",
241
- question_text="How happy would you be with different numbers of children?",
242
- question_items=[
243
- "No children",
244
- "1 child",
245
- "2 children",
246
- "3 or more children",
247
- ],
248
- question_options=[1, 2, 3, 4, 5],
249
- option_labels={1: "Very sad", 3: "Neutral", 5: "Extremely happy"},
250
- )
251
-
252
- def _simulate_answer(self) -> dict:
253
- """Simulate a random valid answer."""
254
- return {
255
- "answer": {
256
- item: random.choice(self.question_options)
257
- for item in self.question_items
258
- }
259
- }
260
-
261
-
262
- if __name__ == "__main__":
263
- import doctest
264
-
265
- doctest.testmod(optionflags=doctest.ELLIPSIS)
@@ -1,28 +0,0 @@
1
- class ResponseValidatorFactory:
2
- def __init__(self, question):
3
- self.question = question
4
-
5
- @property
6
- def response_model(self) -> type["BaseModel"]:
7
- if self.question._response_model is not None:
8
- return self.question._response_model
9
- else:
10
- return self.question.create_response_model()
11
-
12
- @property
13
- def response_validator(self) -> "ResponseValidatorBase":
14
- """Return the response validator."""
15
- params = (
16
- {
17
- "response_model": self.question.response_model,
18
- }
19
- | {k: getattr(self.question, k) for k in self.validator_parameters}
20
- | {"exception_to_throw": getattr(self.question, "exception_to_throw", None)}
21
- | {"override_answer": getattr(self.question, "override_answer", None)}
22
- )
23
- return self.question.response_validator_class(**params)
24
-
25
- @property
26
- def validator_parameters(self) -> list[str]:
27
- """Return the parameters required for the response validator."""
28
- return self.question.response_validator_class.required_params
@@ -1 +0,0 @@
1
-
@@ -1,5 +0,0 @@
1
- Please respond with a dictionary mapping row codes to column codes. E.g., {"0": 1, "1": 3}
2
-
3
- {% if include_comment %}
4
- After the answer, you can put a comment explaining your choices on the next line.
5
- {% endif %}
@@ -1,20 +0,0 @@
1
- {{question_text}}
2
-
3
- Rows:
4
- {% for item in question_items %}
5
- {{ loop.index0 }}: {{item}}
6
- {% endfor %}
7
-
8
- Columns:
9
- {% for option in question_options %}
10
- {{ loop.index0 }}: {{option}}
11
- {%- if option in option_labels %}
12
- ({{option_labels[option]}})
13
- {%- endif %}
14
- {% endfor %}
15
-
16
-
17
- Select one column option for each row.
18
- {% if required %}
19
- All rows require a response.
20
- {% endif %}
@@ -1,122 +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 MarkdownToDocx:
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
-
20
- def _check_pandoc(self):
21
- """Check if pandoc is installed and accessible."""
22
- try:
23
- subprocess.run(["pandoc", "--version"], capture_output=True, check=True)
24
- except (subprocess.CalledProcessError, FileNotFoundError):
25
- raise RuntimeError(
26
- "Pandoc is not installed or not found in PATH. "
27
- "Please install pandoc before using this converter."
28
- )
29
-
30
- def convert(self, output_path: str, **options) -> bool:
31
- """
32
- Convert the markdown content to DOCX.
33
-
34
- Args:
35
- output_path (str): Path where the DOCX should be saved
36
- **options: Additional conversion options
37
- reference_doc (str): Path to reference docx for styling
38
- toc (bool): Include table of contents (default: False)
39
- number_sections (bool): Number sections (default: False)
40
- highlight_style (str): Code highlighting style (default: "tango")
41
-
42
- Returns:
43
- bool: True if conversion was successful, False otherwise
44
- """
45
- # Ensure output directory exists
46
- output_path = Path(output_path)
47
- output_path.parent.mkdir(parents=True, exist_ok=True)
48
-
49
- # Build pandoc command
50
- cmd = ["pandoc", "-f", "markdown", "-t", "docx", "-o", str(output_path)]
51
-
52
- # Add reference doc if provided
53
- if "reference_doc" in options:
54
- ref_doc = Path(options["reference_doc"])
55
- if ref_doc.exists():
56
- cmd.extend(["--reference-doc", str(ref_doc)])
57
- else:
58
- print(f"Warning: Reference document {ref_doc} not found")
59
-
60
- # Add optional parameters
61
- if options.get("toc", False):
62
- cmd.append("--toc")
63
-
64
- if options.get("number_sections", False):
65
- cmd.append("--number-sections")
66
-
67
- if "highlight_style" in options:
68
- cmd.extend(["--highlight-style", options["highlight_style"]])
69
-
70
- try:
71
- # Run pandoc command
72
- result = subprocess.run(
73
- cmd,
74
- input=self.markdown_content,
75
- text=True,
76
- capture_output=True,
77
- check=True,
78
- )
79
- return True
80
- except subprocess.CalledProcessError as e:
81
- print(f"Error converting markdown to DOCX: {e.stderr}")
82
- return False
83
-
84
- def preview(self) -> str:
85
- """
86
- Generate a temporary DOCX and return its path.
87
-
88
- Returns:
89
- str: Path to the temporary DOCX file
90
- """
91
- temp_dir = tempfile.mkdtemp()
92
- if self.filename:
93
- temp_docx = os.path.join(temp_dir, self.filename)
94
- else:
95
- temp_docx = os.path.join(temp_dir, "preview.docx")
96
-
97
- if self.convert(temp_docx):
98
- from edsl.scenarios.FileStore import FileStore
99
-
100
- return FileStore(path=temp_docx)
101
-
102
- return None
103
-
104
- def create_template(self, output_path: str) -> bool:
105
- """
106
- Create a reference DOCX template that can be modified for styling.
107
-
108
- Args:
109
- output_path (str): Path where the template should be saved
110
-
111
- Returns:
112
- bool: True if template was created successfully, False otherwise
113
- """
114
- try:
115
- cmd = ["pandoc", "--print-default-data-file", "reference.docx"]
116
-
117
- with open(output_path, "wb") as f:
118
- result = subprocess.run(cmd, stdout=f, check=True)
119
- return True
120
- except subprocess.CalledProcessError as e:
121
- print(f"Error creating template: {e.stderr}")
122
- return False