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.
- edsl/Base.py +413 -332
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +57 -49
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +1071 -867
- edsl/agents/AgentList.py +551 -413
- edsl/agents/Invigilator.py +284 -233
- edsl/agents/InvigilatorBase.py +257 -270
- edsl/agents/PromptConstructor.py +272 -354
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/__init__.py +2 -3
- edsl/agents/descriptors.py +99 -99
- edsl/agents/prompt_helpers.py +129 -129
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +130 -117
- edsl/auto/StageBase.py +243 -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 +74 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +218 -224
- edsl/base/Base.py +279 -279
- edsl/config.py +177 -157
- edsl/conversation/Conversation.py +290 -290
- edsl/conversation/car_buying.py +59 -58
- edsl/conversation/chips.py +95 -95
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +1106 -1028
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +573 -555
- edsl/data/CacheEntry.py +230 -233
- edsl/data/CacheHandler.py +168 -149
- edsl/data/RemoteCacheSync.py +186 -78
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +5 -4
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +74 -73
- edsl/enums.py +202 -175
- edsl/exceptions/BaseException.py +21 -21
- edsl/exceptions/__init__.py +54 -54
- edsl/exceptions/agents.py +54 -42
- 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/inference_services.py +5 -0
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +109 -91
- edsl/exceptions/results.py +29 -29
- edsl/exceptions/scenarios.py +29 -22
- edsl/exceptions/surveys.py +37 -37
- edsl/inference_services/AnthropicService.py +106 -87
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +118 -120
- edsl/inference_services/AzureAI.py +215 -217
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +143 -148
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +80 -147
- edsl/inference_services/InferenceServicesCollection.py +138 -97
- edsl/inference_services/MistralAIService.py +120 -123
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +236 -224
- edsl/inference_services/PerplexityService.py +160 -163
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +90 -89
- edsl/inference_services/TogetherAIService.py +172 -170
- edsl/inference_services/data_structures.py +134 -0
- 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/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +43 -56
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +823 -898
- edsl/jobs/JobsChecks.py +172 -147
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +270 -268
- edsl/jobs/JobsRemoteInferenceHandler.py +311 -239
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/async_interview_runner.py +138 -0
- edsl/jobs/buckets/BucketCollection.py +104 -63
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +283 -251
- edsl/jobs/buckets/TokenBucketAPI.py +211 -0
- edsl/jobs/buckets/TokenBucketClient.py +191 -0
- edsl/jobs/check_survey_scenario_compatibility.py +85 -0
- edsl/jobs/data_structures.py +120 -0
- edsl/jobs/decorators.py +35 -0
- edsl/jobs/interviews/Interview.py +396 -661
- 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/jobs_status_enums.py +9 -0
- edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
- edsl/jobs/results_exceptions_handler.py +98 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +151 -466
- edsl/jobs/runners/JobsRunnerStatus.py +297 -330
- edsl/jobs/tasks/QuestionTaskCreator.py +244 -242
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +470 -450
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +161 -163
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +626 -668
- edsl/language_models/ModelList.py +164 -155
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/__init__.py +2 -3
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- edsl/language_models/key_management/KeyLookup.py +63 -0
- edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
- edsl/language_models/key_management/KeyLookupCollection.py +38 -0
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +131 -0
- edsl/language_models/model.py +256 -0
- edsl/language_models/repair.py +156 -156
- edsl/language_models/utilities.py +65 -64
- edsl/notebooks/Notebook.py +263 -258
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +352 -362
- edsl/prompts/__init__.py +2 -2
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +518 -664
- edsl/questions/QuestionBasePromptsMixin.py +221 -217
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +180 -182
- edsl/questions/QuestionFreeText.py +113 -114
- edsl/questions/QuestionFunctional.py +166 -166
- edsl/questions/QuestionList.py +223 -231
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +330 -286
- edsl/questions/QuestionNumerical.py +151 -153
- edsl/questions/QuestionRank.py +314 -324
- edsl/questions/Quick.py +41 -41
- edsl/questions/SimpleAskMixin.py +74 -73
- edsl/questions/__init__.py +27 -26
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +334 -289
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/data_structures.py +20 -0
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +90 -87
- edsl/questions/derived/QuestionTopK.py +93 -93
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +427 -413
- edsl/questions/loop_processor.py +149 -0
- 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/{QuestionBaseGenMixin.py → question_base_gen_mixin.py} +168 -161
- edsl/questions/question_registry.py +177 -177
- edsl/questions/{RegisterQuestionsMeta.py → register_questions_meta.py} +71 -71
- edsl/questions/{ResponseValidatorABC.py → response_validator_abc.py} +188 -174
- edsl/questions/response_validator_factory.py +34 -0
- 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/matrix/__init__.py +1 -0
- edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
- edsl/questions/templates/matrix/question_presentation.jinja +20 -0
- 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 +587 -424
- edsl/results/DatasetExportMixin.py +594 -731
- edsl/results/DatasetTree.py +295 -275
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +557 -465
- edsl/results/Results.py +1183 -1165
- edsl/results/ResultsExportMixin.py +45 -43
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/TableDisplay.py +125 -198
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +2 -2
- edsl/results/file_exports.py +252 -0
- edsl/results/{ResultsFetchMixin.py → results_fetch_mixin.py} +33 -33
- edsl/results/{Selector.py → results_selector.py} +145 -135
- edsl/results/{ResultsToolsMixin.py → results_tools_mixin.py} +98 -98
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_display.css +77 -77
- edsl/results/table_renderers.py +118 -0
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +511 -632
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +498 -601
- edsl/scenarios/ScenarioHtmlMixin.py +65 -64
- edsl/scenarios/ScenarioList.py +1458 -1287
- edsl/scenarios/ScenarioListExportMixin.py +45 -52
- edsl/scenarios/ScenarioListPdfMixin.py +239 -261
- edsl/scenarios/__init__.py +3 -4
- edsl/scenarios/directory_scanner.py +96 -0
- edsl/scenarios/file_methods.py +85 -0
- edsl/scenarios/handlers/__init__.py +13 -0
- edsl/scenarios/handlers/csv.py +38 -0
- edsl/scenarios/handlers/docx.py +76 -0
- edsl/scenarios/handlers/html.py +37 -0
- edsl/scenarios/handlers/json.py +111 -0
- edsl/scenarios/handlers/latex.py +5 -0
- edsl/scenarios/handlers/md.py +51 -0
- edsl/scenarios/handlers/pdf.py +68 -0
- edsl/scenarios/handlers/png.py +39 -0
- edsl/scenarios/handlers/pptx.py +105 -0
- edsl/scenarios/handlers/py.py +294 -0
- edsl/scenarios/handlers/sql.py +313 -0
- edsl/scenarios/handlers/sqlite.py +149 -0
- edsl/scenarios/handlers/txt.py +33 -0
- edsl/scenarios/{ScenarioJoin.py → scenario_join.py} +131 -127
- edsl/scenarios/scenario_selector.py +156 -0
- 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 +521 -528
- edsl/study/__init__.py +4 -4
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +327 -326
- edsl/surveys/RuleCollection.py +385 -387
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +1280 -1801
- edsl/surveys/SurveyCSS.py +273 -261
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +181 -179
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +5 -3
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +60 -56
- edsl/surveys/instructions/ChangeInstruction.py +48 -49
- edsl/surveys/instructions/Instruction.py +56 -65
- edsl/surveys/instructions/InstructionCollection.py +82 -77
- 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/PrettyList.py +56 -0
- 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/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/naming_utilities.py +263 -263
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +436 -424
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/LICENSE +21 -21
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/METADATA +13 -11
- edsl-0.1.39.dev5.dist-info/RECORD +358 -0
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/WHEEL +1 -1
- edsl/language_models/KeyLookup.py +0 -30
- edsl/language_models/registry.py +0 -190
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/results/ResultsDBMixin.py +0 -238
- edsl-0.1.39.dev3.dist-info/RECORD +0 -277
@@ -0,0 +1,122 @@
|
|
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
|
@@ -0,0 +1,111 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
import subprocess
|
3
|
+
import os
|
4
|
+
from pathlib import Path
|
5
|
+
import tempfile
|
6
|
+
|
7
|
+
|
8
|
+
class MarkdownToPDF:
|
9
|
+
def __init__(self, markdown_content: str, filename: Optional[str] = None):
|
10
|
+
"""
|
11
|
+
Initialize the converter with markdown content.
|
12
|
+
|
13
|
+
Args:
|
14
|
+
markdown_content (str): The markdown content to be converted
|
15
|
+
"""
|
16
|
+
self.markdown_content = markdown_content
|
17
|
+
self.filename = filename
|
18
|
+
self._check_pandoc()
|
19
|
+
# self.convert()
|
20
|
+
|
21
|
+
def _check_pandoc(self):
|
22
|
+
"""Check if pandoc is installed and accessible."""
|
23
|
+
try:
|
24
|
+
subprocess.run(["pandoc", "--version"], capture_output=True, check=True)
|
25
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
26
|
+
raise RuntimeError(
|
27
|
+
"Pandoc is not installed or not found in PATH. "
|
28
|
+
"Please install pandoc before using this converter."
|
29
|
+
)
|
30
|
+
|
31
|
+
def convert(self, output_path: str, **options) -> bool:
|
32
|
+
"""
|
33
|
+
Convert the markdown content to PDF.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
output_path (str): Path where the PDF should be saved
|
37
|
+
**options: Additional conversion options
|
38
|
+
margin (str): Page margin (default: "1in")
|
39
|
+
font_size (str): Font size (default: "12pt")
|
40
|
+
toc (bool): Include table of contents (default: False)
|
41
|
+
number_sections (bool): Number sections (default: False)
|
42
|
+
highlight_style (str): Code highlighting style (default: "tango")
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
bool: True if conversion was successful, False otherwise
|
46
|
+
"""
|
47
|
+
# Ensure output directory exists
|
48
|
+
output_path = Path(output_path)
|
49
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
50
|
+
|
51
|
+
# Build pandoc command with default options
|
52
|
+
cmd = [
|
53
|
+
"pandoc",
|
54
|
+
"-f",
|
55
|
+
"markdown",
|
56
|
+
"-o",
|
57
|
+
str(output_path),
|
58
|
+
"--pdf-engine=xelatex",
|
59
|
+
"--variable",
|
60
|
+
f'geometry:margin={options.get("margin", "1in")}',
|
61
|
+
"--variable",
|
62
|
+
f'fontsize={options.get("font_size", "12pt")}',
|
63
|
+
]
|
64
|
+
|
65
|
+
# Add font only if specifically provided
|
66
|
+
if "font" in options:
|
67
|
+
cmd.extend(["--variable", f'mainfont={options["font"]}'])
|
68
|
+
|
69
|
+
# Add optional parameters
|
70
|
+
if options.get("toc", False):
|
71
|
+
cmd.append("--toc")
|
72
|
+
|
73
|
+
if options.get("number_sections", False):
|
74
|
+
cmd.append("--number-sections")
|
75
|
+
|
76
|
+
if "highlight_style" in options:
|
77
|
+
cmd.extend(["--highlight-style", options["highlight_style"]])
|
78
|
+
|
79
|
+
try:
|
80
|
+
# Run pandoc command
|
81
|
+
result = subprocess.run(
|
82
|
+
cmd,
|
83
|
+
input=self.markdown_content,
|
84
|
+
text=True,
|
85
|
+
capture_output=True,
|
86
|
+
check=True,
|
87
|
+
)
|
88
|
+
return True
|
89
|
+
except subprocess.CalledProcessError as e:
|
90
|
+
print(f"Error converting markdown to PDF: {e.stderr}")
|
91
|
+
return False
|
92
|
+
|
93
|
+
def preview(self) -> str:
|
94
|
+
"""
|
95
|
+
Generate a temporary PDF and return its path.
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
str: Path to the temporary PDF file
|
99
|
+
"""
|
100
|
+
temp_dir = tempfile.mkdtemp()
|
101
|
+
if self.filename:
|
102
|
+
temp_pdf = os.path.join(temp_dir, f"{self.filename}.pdf")
|
103
|
+
else:
|
104
|
+
temp_pdf = os.path.join(temp_dir, "preview.pdf")
|
105
|
+
|
106
|
+
if self.convert(temp_pdf):
|
107
|
+
from edsl.scenarios.FileStore import FileStore
|
108
|
+
|
109
|
+
return FileStore(temp_pdf)
|
110
|
+
|
111
|
+
return None
|