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.
- edsl/Base.py +332 -385
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +49 -57
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +867 -1079
- edsl/agents/AgentList.py +413 -551
- edsl/agents/Invigilator.py +233 -285
- edsl/agents/InvigilatorBase.py +270 -254
- edsl/agents/PromptConstructor.py +354 -252
- edsl/agents/__init__.py +3 -2
- edsl/agents/descriptors.py +99 -99
- edsl/agents/prompt_helpers.py +129 -129
- edsl/auto/AutoStudy.py +117 -117
- edsl/auto/StageBase.py +230 -230
- edsl/auto/StageGenerateSurvey.py +178 -178
- edsl/auto/StageLabelQuestions.py +125 -125
- edsl/auto/StagePersona.py +61 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
- edsl/auto/StagePersonaDimensionValues.py +74 -74
- edsl/auto/StagePersonaDimensions.py +69 -69
- edsl/auto/StageQuestions.py +73 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +224 -224
- edsl/base/Base.py +279 -279
- edsl/config.py +157 -177
- edsl/conversation/Conversation.py +290 -290
- edsl/conversation/car_buying.py +58 -59
- edsl/conversation/chips.py +95 -95
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +1028 -1090
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +555 -562
- edsl/data/CacheEntry.py +233 -230
- edsl/data/CacheHandler.py +149 -170
- edsl/data/RemoteCacheSync.py +78 -78
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +4 -5
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +73 -74
- edsl/enums.py +175 -195
- edsl/exceptions/BaseException.py +21 -21
- edsl/exceptions/__init__.py +54 -54
- edsl/exceptions/agents.py +42 -54
- edsl/exceptions/cache.py +5 -5
- edsl/exceptions/configuration.py +16 -16
- edsl/exceptions/coop.py +10 -10
- edsl/exceptions/data.py +14 -14
- edsl/exceptions/general.py +34 -34
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +91 -109
- edsl/exceptions/results.py +29 -29
- edsl/exceptions/scenarios.py +22 -29
- edsl/exceptions/surveys.py +37 -37
- edsl/inference_services/AnthropicService.py +87 -84
- edsl/inference_services/AwsBedrock.py +120 -118
- edsl/inference_services/AzureAI.py +217 -215
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +148 -139
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +147 -80
- edsl/inference_services/InferenceServicesCollection.py +97 -122
- edsl/inference_services/MistralAIService.py +123 -120
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +224 -221
- edsl/inference_services/PerplexityService.py +163 -160
- edsl/inference_services/TestService.py +89 -92
- edsl/inference_services/TogetherAIService.py +170 -170
- edsl/inference_services/models_available_cache.py +118 -118
- edsl/inference_services/rate_limits_cache.py +25 -25
- edsl/inference_services/registry.py +41 -41
- edsl/inference_services/write_available.py +10 -10
- edsl/jobs/Answers.py +56 -43
- edsl/jobs/Jobs.py +898 -757
- edsl/jobs/JobsChecks.py +147 -172
- edsl/jobs/JobsPrompts.py +268 -270
- edsl/jobs/JobsRemoteInferenceHandler.py +239 -287
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/buckets/BucketCollection.py +63 -104
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +251 -283
- edsl/jobs/interviews/Interview.py +661 -358
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
- edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
- edsl/jobs/interviews/InterviewStatistic.py +63 -63
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
- edsl/jobs/interviews/InterviewStatusLog.py +92 -92
- edsl/jobs/interviews/ReportErrors.py +66 -66
- edsl/jobs/interviews/interview_status_enum.py +9 -9
- edsl/jobs/runners/JobsRunnerAsyncio.py +466 -421
- edsl/jobs/runners/JobsRunnerStatus.py +330 -330
- edsl/jobs/tasks/QuestionTaskCreator.py +242 -244
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +450 -449
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +163 -161
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/KeyLookup.py +30 -0
- edsl/language_models/LanguageModel.py +668 -571
- edsl/language_models/ModelList.py +155 -153
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/__init__.py +3 -2
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- edsl/language_models/registry.py +190 -180
- edsl/language_models/repair.py +156 -156
- edsl/language_models/unused/ReplicateBase.py +83 -0
- edsl/language_models/utilities.py +64 -65
- edsl/notebooks/Notebook.py +258 -263
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +362 -352
- edsl/prompts/__init__.py +2 -2
- edsl/questions/AnswerValidatorMixin.py +289 -334
- edsl/questions/QuestionBase.py +664 -509
- edsl/questions/QuestionBaseGenMixin.py +161 -165
- edsl/questions/QuestionBasePromptsMixin.py +217 -221
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +182 -182
- edsl/questions/QuestionFreeText.py +114 -113
- edsl/questions/QuestionFunctional.py +166 -166
- edsl/questions/QuestionList.py +231 -229
- edsl/questions/QuestionMultipleChoice.py +286 -330
- edsl/questions/QuestionNumerical.py +153 -151
- edsl/questions/QuestionRank.py +324 -314
- edsl/questions/Quick.py +41 -41
- edsl/questions/RegisterQuestionsMeta.py +71 -71
- edsl/questions/ResponseValidatorABC.py +174 -200
- edsl/questions/SimpleAskMixin.py +73 -74
- edsl/questions/__init__.py +26 -27
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +87 -90
- edsl/questions/derived/QuestionTopK.py +93 -93
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +413 -427
- edsl/questions/prompt_templates/question_budget.jinja +13 -13
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
- edsl/questions/prompt_templates/question_extract.jinja +11 -11
- edsl/questions/prompt_templates/question_free_text.jinja +3 -3
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
- edsl/questions/prompt_templates/question_list.jinja +17 -17
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
- edsl/questions/prompt_templates/question_numerical.jinja +36 -36
- edsl/questions/question_registry.py +177 -177
- edsl/questions/settings.py +12 -12
- edsl/questions/templates/budget/answering_instructions.jinja +7 -7
- edsl/questions/templates/budget/question_presentation.jinja +7 -7
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
- edsl/questions/templates/extract/answering_instructions.jinja +7 -7
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
- edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
- edsl/questions/templates/list/answering_instructions.jinja +3 -3
- edsl/questions/templates/list/question_presentation.jinja +5 -5
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
- edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
- edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
- edsl/questions/templates/numerical/question_presentation.jinja +6 -6
- edsl/questions/templates/rank/answering_instructions.jinja +11 -11
- edsl/questions/templates/rank/question_presentation.jinja +15 -15
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
- edsl/questions/templates/top_k/question_presentation.jinja +22 -22
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
- edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
- edsl/results/CSSParameterizer.py +108 -108
- edsl/results/Dataset.py +424 -587
- edsl/results/DatasetExportMixin.py +731 -653
- edsl/results/DatasetTree.py +275 -295
- edsl/results/Result.py +465 -451
- edsl/results/Results.py +1165 -1172
- edsl/results/ResultsDBMixin.py +238 -0
- edsl/results/ResultsExportMixin.py +43 -45
- edsl/results/ResultsFetchMixin.py +33 -33
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/ResultsToolsMixin.py +98 -98
- edsl/results/Selector.py +135 -145
- edsl/results/TableDisplay.py +198 -125
- edsl/results/__init__.py +2 -2
- edsl/results/table_display.css +77 -77
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/FileStore.py +632 -511
- edsl/scenarios/Scenario.py +601 -498
- edsl/scenarios/ScenarioHtmlMixin.py +64 -65
- edsl/scenarios/ScenarioJoin.py +127 -131
- edsl/scenarios/ScenarioList.py +1287 -1430
- edsl/scenarios/ScenarioListExportMixin.py +52 -45
- edsl/scenarios/ScenarioListPdfMixin.py +261 -239
- edsl/scenarios/__init__.py +4 -3
- edsl/shared.py +1 -1
- edsl/study/ObjectEntry.py +173 -173
- edsl/study/ProofOfWork.py +113 -113
- edsl/study/SnapShot.py +80 -80
- edsl/study/Study.py +528 -521
- edsl/study/__init__.py +4 -4
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +326 -327
- edsl/surveys/RuleCollection.py +387 -385
- edsl/surveys/Survey.py +1801 -1229
- edsl/surveys/SurveyCSS.py +261 -273
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +179 -181
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/__init__.py +3 -5
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +56 -60
- edsl/surveys/instructions/ChangeInstruction.py +49 -48
- edsl/surveys/instructions/Instruction.py +65 -56
- edsl/surveys/instructions/InstructionCollection.py +77 -82
- edsl/templates/error_reporting/base.html +23 -23
- edsl/templates/error_reporting/exceptions_by_model.html +34 -34
- edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
- edsl/templates/error_reporting/exceptions_by_type.html +16 -16
- edsl/templates/error_reporting/interview_details.html +115 -115
- edsl/templates/error_reporting/interviews.html +19 -19
- edsl/templates/error_reporting/overview.html +4 -4
- edsl/templates/error_reporting/performance_plot.html +1 -1
- edsl/templates/error_reporting/report.css +73 -73
- edsl/templates/error_reporting/report.html +117 -117
- edsl/templates/error_reporting/report.js +25 -25
- edsl/tools/__init__.py +1 -1
- edsl/tools/clusters.py +192 -192
- edsl/tools/embeddings.py +27 -27
- edsl/tools/embeddings_plotting.py +118 -118
- edsl/tools/plotting.py +112 -112
- edsl/tools/summarize.py +18 -18
- edsl/utilities/SystemInfo.py +28 -28
- edsl/utilities/__init__.py +22 -22
- edsl/utilities/ast_utilities.py +25 -25
- edsl/utilities/data/Registry.py +6 -6
- edsl/utilities/data/__init__.py +1 -1
- edsl/utilities/data/scooter_results.json +1 -1
- edsl/utilities/decorators.py +77 -77
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
- edsl/utilities/interface.py +627 -627
- edsl/utilities/naming_utilities.py +263 -263
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +424 -436
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/LICENSE +21 -21
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/METADATA +10 -12
- edsl-0.1.39.dev3.dist-info/RECORD +277 -0
- edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
- edsl/agents/QuestionOptionProcessor.py +0 -172
- edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
- edsl/coop/CoopFunctionsMixin.py +0 -15
- edsl/coop/ExpectedParrotKeyHandler.py +0 -125
- edsl/exceptions/inference_services.py +0 -5
- edsl/inference_services/AvailableModelCacheHandler.py +0 -184
- edsl/inference_services/AvailableModelFetcher.py +0 -209
- edsl/inference_services/ServiceAvailability.py +0 -135
- edsl/inference_services/data_structures.py +0 -62
- edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -188
- edsl/jobs/FetchInvigilator.py +0 -40
- edsl/jobs/InterviewTaskManager.py +0 -98
- edsl/jobs/InterviewsConstructor.py +0 -48
- edsl/jobs/JobsComponentConstructor.py +0 -189
- edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
- edsl/jobs/RequestTokenEstimator.py +0 -30
- edsl/jobs/buckets/TokenBucketAPI.py +0 -211
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/decorators.py +0 -35
- edsl/jobs/jobs_status_enums.py +0 -9
- edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
- edsl/language_models/ComputeCost.py +0 -63
- edsl/language_models/PriceManager.py +0 -127
- edsl/language_models/RawResponseHandler.py +0 -106
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +0 -131
- edsl/notebooks/NotebookToLaTeX.py +0 -142
- edsl/questions/ExceptionExplainer.py +0 -77
- edsl/questions/HTMLQuestion.py +0 -103
- edsl/questions/LoopProcessor.py +0 -149
- edsl/questions/QuestionMatrix.py +0 -265
- edsl/questions/ResponseValidatorFactory.py +0 -28
- edsl/questions/templates/matrix/__init__.py +0 -1
- edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
- edsl/questions/templates/matrix/question_presentation.jinja +0 -20
- edsl/results/MarkdownToDocx.py +0 -122
- edsl/results/MarkdownToPDF.py +0 -111
- edsl/results/TextEditor.py +0 -50
- edsl/results/smart_objects.py +0 -96
- edsl/results/table_data_class.py +0 -12
- edsl/results/table_renderers.py +0 -118
- edsl/scenarios/ConstructDownloadLink.py +0 -109
- edsl/scenarios/DirectoryScanner.py +0 -96
- edsl/scenarios/DocumentChunker.py +0 -102
- edsl/scenarios/DocxScenario.py +0 -16
- edsl/scenarios/PdfExtractor.py +0 -40
- edsl/scenarios/ScenarioSelector.py +0 -156
- edsl/scenarios/file_methods.py +0 -85
- edsl/scenarios/handlers/__init__.py +0 -13
- edsl/scenarios/handlers/csv.py +0 -38
- edsl/scenarios/handlers/docx.py +0 -76
- edsl/scenarios/handlers/html.py +0 -37
- edsl/scenarios/handlers/json.py +0 -111
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/scenarios/handlers/md.py +0 -51
- edsl/scenarios/handlers/pdf.py +0 -68
- edsl/scenarios/handlers/png.py +0 -39
- edsl/scenarios/handlers/pptx.py +0 -105
- edsl/scenarios/handlers/py.py +0 -294
- edsl/scenarios/handlers/sql.py +0 -313
- edsl/scenarios/handlers/sqlite.py +0 -149
- edsl/scenarios/handlers/txt.py +0 -33
- edsl/surveys/ConstructDAG.py +0 -92
- edsl/surveys/EditSurvey.py +0 -221
- edsl/surveys/InstructionHandler.py +0 -100
- edsl/surveys/MemoryManagement.py +0 -72
- edsl/surveys/RuleManager.py +0 -172
- edsl/surveys/Simulator.py +0 -75
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/utilities/PrettyList.py +0 -56
- edsl/utilities/is_notebook.py +0 -18
- edsl/utilities/is_valid_variable_name.py +0 -11
- edsl/utilities/remove_edsl_version.py +0 -24
- edsl-0.1.39.dev2.dist-info/RECORD +0 -352
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/WHEEL +0 -0
edsl/questions/HTMLQuestion.py
DELETED
@@ -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
|
edsl/questions/LoopProcessor.py
DELETED
@@ -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
|
-
"""
|
edsl/questions/QuestionMatrix.py
DELETED
@@ -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,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 %}
|
edsl/results/MarkdownToDocx.py
DELETED
@@ -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
|