edsl 0.1.15__py3-none-any.whl → 0.1.40__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 +348 -38
- edsl/BaseDiff.py +260 -0
- edsl/TemplateLoader.py +24 -0
- edsl/__init__.py +45 -10
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +842 -144
- edsl/agents/AgentList.py +521 -25
- edsl/agents/Invigilator.py +250 -374
- edsl/agents/InvigilatorBase.py +257 -0
- edsl/agents/PromptConstructor.py +272 -0
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/descriptors.py +43 -13
- edsl/agents/prompt_helpers.py +129 -0
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +130 -0
- edsl/auto/StageBase.py +243 -0
- edsl/auto/StageGenerateSurvey.py +178 -0
- edsl/auto/StageLabelQuestions.py +125 -0
- edsl/auto/StagePersona.py +61 -0
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
- edsl/auto/StagePersonaDimensionValues.py +74 -0
- edsl/auto/StagePersonaDimensions.py +69 -0
- edsl/auto/StageQuestions.py +74 -0
- edsl/auto/SurveyCreatorPipeline.py +21 -0
- edsl/auto/utilities.py +218 -0
- edsl/base/Base.py +279 -0
- edsl/config.py +115 -113
- edsl/conversation/Conversation.py +290 -0
- edsl/conversation/car_buying.py +59 -0
- edsl/conversation/chips.py +95 -0
- edsl/conversation/mug_negotiation.py +81 -0
- edsl/conversation/next_speaker_utilities.py +93 -0
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +54 -0
- edsl/coop/__init__.py +1 -0
- edsl/coop/coop.py +1029 -134
- edsl/coop/utils.py +131 -0
- edsl/data/Cache.py +560 -89
- edsl/data/CacheEntry.py +230 -0
- edsl/data/CacheHandler.py +168 -0
- edsl/data/RemoteCacheSync.py +186 -0
- edsl/data/SQLiteDict.py +292 -0
- edsl/data/__init__.py +5 -3
- edsl/data/orm.py +6 -33
- edsl/data_transfer_models.py +74 -27
- edsl/enums.py +165 -8
- edsl/exceptions/BaseException.py +21 -0
- edsl/exceptions/__init__.py +52 -46
- edsl/exceptions/agents.py +33 -15
- edsl/exceptions/cache.py +5 -0
- edsl/exceptions/coop.py +8 -0
- edsl/exceptions/general.py +34 -0
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/jobs.py +15 -0
- edsl/exceptions/language_models.py +46 -1
- edsl/exceptions/questions.py +80 -5
- edsl/exceptions/results.py +16 -5
- edsl/exceptions/scenarios.py +29 -0
- edsl/exceptions/surveys.py +13 -10
- edsl/inference_services/AnthropicService.py +106 -0
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +118 -0
- edsl/inference_services/AzureAI.py +215 -0
- edsl/inference_services/DeepInfraService.py +18 -0
- edsl/inference_services/GoogleService.py +143 -0
- edsl/inference_services/GroqService.py +20 -0
- edsl/inference_services/InferenceServiceABC.py +80 -0
- edsl/inference_services/InferenceServicesCollection.py +138 -0
- edsl/inference_services/MistralAIService.py +120 -0
- edsl/inference_services/OllamaService.py +18 -0
- edsl/inference_services/OpenAIService.py +236 -0
- edsl/inference_services/PerplexityService.py +160 -0
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +90 -0
- edsl/inference_services/TogetherAIService.py +172 -0
- edsl/inference_services/data_structures.py +134 -0
- edsl/inference_services/models_available_cache.py +118 -0
- edsl/inference_services/rate_limits_cache.py +25 -0
- edsl/inference_services/registry.py +41 -0
- edsl/inference_services/write_available.py +10 -0
- edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +21 -20
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +684 -206
- edsl/jobs/JobsChecks.py +172 -0
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +270 -0
- edsl/jobs/JobsRemoteInferenceHandler.py +311 -0
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/async_interview_runner.py +138 -0
- edsl/jobs/buckets/BucketCollection.py +104 -0
- edsl/jobs/buckets/ModelBuckets.py +65 -0
- edsl/jobs/buckets/TokenBucket.py +283 -0
- 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 +392 -0
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -0
- edsl/jobs/interviews/InterviewExceptionEntry.py +186 -0
- edsl/jobs/interviews/InterviewStatistic.py +63 -0
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -0
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -0
- edsl/jobs/interviews/InterviewStatusLog.py +92 -0
- edsl/jobs/interviews/ReportErrors.py +66 -0
- edsl/jobs/interviews/interview_status_enum.py +9 -0
- 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 -110
- edsl/jobs/runners/JobsRunnerStatus.py +298 -0
- edsl/jobs/tasks/QuestionTaskCreator.py +244 -0
- edsl/jobs/tasks/TaskCreators.py +64 -0
- edsl/jobs/tasks/TaskHistory.py +470 -0
- edsl/jobs/tasks/TaskStatusLog.py +23 -0
- edsl/jobs/tasks/task_status_enum.py +161 -0
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -0
- edsl/jobs/tokens/TokenUsage.py +34 -0
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +507 -386
- edsl/language_models/ModelList.py +164 -0
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -0
- edsl/language_models/__init__.py +1 -8
- edsl/language_models/fake_openai_call.py +15 -0
- edsl/language_models/fake_openai_service.py +61 -0
- 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 +109 -41
- edsl/language_models/utilities.py +65 -0
- edsl/notebooks/Notebook.py +263 -0
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/notebooks/__init__.py +1 -0
- edsl/prompts/Prompt.py +222 -93
- edsl/prompts/__init__.py +1 -1
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +518 -0
- edsl/questions/QuestionBasePromptsMixin.py +221 -0
- edsl/questions/QuestionBudget.py +164 -67
- edsl/questions/QuestionCheckBox.py +281 -62
- edsl/questions/QuestionDict.py +343 -0
- edsl/questions/QuestionExtract.py +136 -50
- edsl/questions/QuestionFreeText.py +79 -55
- edsl/questions/QuestionFunctional.py +138 -41
- edsl/questions/QuestionList.py +184 -57
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +293 -69
- edsl/questions/QuestionNumerical.py +109 -56
- edsl/questions/QuestionRank.py +244 -49
- edsl/questions/Quick.py +41 -0
- edsl/questions/SimpleAskMixin.py +74 -0
- edsl/questions/__init__.py +9 -6
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +153 -38
- edsl/questions/compose_questions.py +13 -7
- edsl/questions/data_structures.py +20 -0
- edsl/questions/decorators.py +21 -0
- edsl/questions/derived/QuestionLikertFive.py +28 -26
- edsl/questions/derived/QuestionLinearScale.py +41 -28
- edsl/questions/derived/QuestionTopK.py +34 -26
- edsl/questions/derived/QuestionYesNo.py +40 -27
- edsl/questions/descriptors.py +228 -74
- edsl/questions/loop_processor.py +149 -0
- edsl/questions/prompt_templates/question_budget.jinja +13 -0
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
- edsl/questions/prompt_templates/question_extract.jinja +11 -0
- edsl/questions/prompt_templates/question_free_text.jinja +3 -0
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
- edsl/questions/prompt_templates/question_list.jinja +17 -0
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
- edsl/questions/prompt_templates/question_numerical.jinja +37 -0
- edsl/questions/question_base_gen_mixin.py +168 -0
- edsl/questions/question_registry.py +130 -46
- edsl/questions/register_questions_meta.py +71 -0
- edsl/questions/response_validator_abc.py +188 -0
- edsl/questions/response_validator_factory.py +34 -0
- edsl/questions/settings.py +5 -2
- edsl/questions/templates/__init__.py +0 -0
- edsl/questions/templates/budget/__init__.py +0 -0
- edsl/questions/templates/budget/answering_instructions.jinja +7 -0
- edsl/questions/templates/budget/question_presentation.jinja +7 -0
- edsl/questions/templates/checkbox/__init__.py +0 -0
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
- edsl/questions/templates/dict/__init__.py +0 -0
- edsl/questions/templates/dict/answering_instructions.jinja +21 -0
- edsl/questions/templates/dict/question_presentation.jinja +1 -0
- edsl/questions/templates/extract/__init__.py +0 -0
- edsl/questions/templates/extract/answering_instructions.jinja +7 -0
- edsl/questions/templates/extract/question_presentation.jinja +1 -0
- edsl/questions/templates/free_text/__init__.py +0 -0
- edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
- edsl/questions/templates/free_text/question_presentation.jinja +1 -0
- edsl/questions/templates/likert_five/__init__.py +0 -0
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
- edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
- edsl/questions/templates/linear_scale/__init__.py +0 -0
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
- edsl/questions/templates/list/__init__.py +0 -0
- edsl/questions/templates/list/answering_instructions.jinja +4 -0
- edsl/questions/templates/list/question_presentation.jinja +5 -0
- 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/__init__.py +0 -0
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
- edsl/questions/templates/multiple_choice/html.jinja +0 -0
- edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
- edsl/questions/templates/numerical/__init__.py +0 -0
- edsl/questions/templates/numerical/answering_instructions.jinja +7 -0
- edsl/questions/templates/numerical/question_presentation.jinja +7 -0
- edsl/questions/templates/rank/__init__.py +0 -0
- edsl/questions/templates/rank/answering_instructions.jinja +11 -0
- edsl/questions/templates/rank/question_presentation.jinja +15 -0
- edsl/questions/templates/top_k/__init__.py +0 -0
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
- edsl/questions/templates/top_k/question_presentation.jinja +22 -0
- edsl/questions/templates/yes_no/__init__.py +0 -0
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
- edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
- edsl/results/CSSParameterizer.py +108 -0
- edsl/results/Dataset.py +550 -19
- edsl/results/DatasetExportMixin.py +594 -0
- edsl/results/DatasetTree.py +295 -0
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +477 -173
- edsl/results/Results.py +987 -269
- edsl/results/ResultsExportMixin.py +28 -125
- edsl/results/ResultsGGMixin.py +83 -15
- edsl/results/TableDisplay.py +125 -0
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +1 -1
- edsl/results/file_exports.py +252 -0
- edsl/results/results_fetch_mixin.py +33 -0
- edsl/results/results_selector.py +145 -0
- edsl/results/results_tools_mixin.py +98 -0
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_display.css +78 -0
- edsl/results/table_renderers.py +118 -0
- edsl/results/tree_explore.py +115 -0
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +543 -0
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +431 -62
- edsl/scenarios/ScenarioHtmlMixin.py +65 -0
- edsl/scenarios/ScenarioList.py +1415 -45
- edsl/scenarios/ScenarioListExportMixin.py +45 -0
- edsl/scenarios/ScenarioListPdfMixin.py +239 -0
- edsl/scenarios/__init__.py +2 -0
- 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 +49 -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/scenario_join.py +131 -0
- edsl/scenarios/scenario_selector.py +156 -0
- edsl/shared.py +1 -0
- edsl/study/ObjectEntry.py +173 -0
- edsl/study/ProofOfWork.py +113 -0
- edsl/study/SnapShot.py +80 -0
- edsl/study/Study.py +521 -0
- edsl/study/__init__.py +4 -0
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/DAG.py +92 -11
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/Memory.py +9 -4
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/MemoryPlan.py +156 -35
- edsl/surveys/Rule.py +221 -74
- edsl/surveys/RuleCollection.py +241 -61
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +1079 -339
- edsl/surveys/SurveyCSS.py +273 -0
- edsl/surveys/SurveyExportMixin.py +235 -40
- edsl/surveys/SurveyFlowVisualization.py +181 -0
- edsl/surveys/SurveyQualtricsImport.py +284 -0
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +4 -2
- edsl/surveys/base.py +19 -3
- edsl/surveys/descriptors.py +17 -6
- edsl/surveys/instructions/ChangeInstruction.py +48 -0
- edsl/surveys/instructions/Instruction.py +56 -0
- edsl/surveys/instructions/InstructionCollection.py +82 -0
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/templates/error_reporting/base.html +24 -0
- edsl/templates/error_reporting/exceptions_by_model.html +35 -0
- edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
- edsl/templates/error_reporting/exceptions_by_type.html +17 -0
- edsl/templates/error_reporting/interview_details.html +116 -0
- edsl/templates/error_reporting/interviews.html +19 -0
- edsl/templates/error_reporting/overview.html +5 -0
- edsl/templates/error_reporting/performance_plot.html +2 -0
- edsl/templates/error_reporting/report.css +74 -0
- edsl/templates/error_reporting/report.html +118 -0
- edsl/templates/error_reporting/report.js +25 -0
- edsl/tools/__init__.py +1 -0
- edsl/tools/clusters.py +192 -0
- edsl/tools/embeddings.py +27 -0
- edsl/tools/embeddings_plotting.py +118 -0
- edsl/tools/plotting.py +112 -0
- edsl/tools/summarize.py +18 -0
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/SystemInfo.py +5 -0
- edsl/utilities/__init__.py +21 -20
- edsl/utilities/ast_utilities.py +3 -0
- edsl/utilities/data/Registry.py +2 -0
- edsl/utilities/decorators.py +41 -0
- edsl/utilities/gcp_bucket/__init__.py +0 -0
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -0
- edsl/utilities/interface.py +310 -60
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/naming_utilities.py +263 -0
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/repair_functions.py +28 -0
- edsl/utilities/restricted_python.py +70 -0
- edsl/utilities/utilities.py +203 -13
- edsl-0.1.40.dist-info/METADATA +111 -0
- edsl-0.1.40.dist-info/RECORD +362 -0
- {edsl-0.1.15.dist-info → edsl-0.1.40.dist-info}/WHEEL +1 -1
- edsl/agents/AgentListExportMixin.py +0 -24
- edsl/coop/old.py +0 -31
- edsl/data/Database.py +0 -141
- edsl/data/crud.py +0 -121
- edsl/jobs/Interview.py +0 -435
- edsl/jobs/JobsRunner.py +0 -63
- edsl/jobs/JobsRunnerStatusMixin.py +0 -115
- edsl/jobs/base.py +0 -47
- edsl/jobs/buckets.py +0 -178
- edsl/jobs/runners/JobsRunnerDryRun.py +0 -19
- edsl/jobs/runners/JobsRunnerStreaming.py +0 -54
- edsl/jobs/task_management.py +0 -215
- edsl/jobs/token_tracking.py +0 -78
- edsl/language_models/DeepInfra.py +0 -69
- edsl/language_models/OpenAI.py +0 -98
- edsl/language_models/model_interfaces/GeminiPro.py +0 -66
- edsl/language_models/model_interfaces/LanguageModelOpenAIFour.py +0 -8
- edsl/language_models/model_interfaces/LanguageModelOpenAIThreeFiveTurbo.py +0 -8
- edsl/language_models/model_interfaces/LlamaTwo13B.py +0 -21
- edsl/language_models/model_interfaces/LlamaTwo70B.py +0 -21
- edsl/language_models/model_interfaces/Mixtral8x7B.py +0 -24
- edsl/language_models/registry.py +0 -81
- edsl/language_models/schemas.py +0 -15
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/prompts/QuestionInstructionsBase.py +0 -6
- edsl/prompts/library/agent_instructions.py +0 -29
- edsl/prompts/library/agent_persona.py +0 -17
- edsl/prompts/library/question_budget.py +0 -26
- edsl/prompts/library/question_checkbox.py +0 -32
- edsl/prompts/library/question_extract.py +0 -19
- edsl/prompts/library/question_freetext.py +0 -14
- edsl/prompts/library/question_linear_scale.py +0 -20
- edsl/prompts/library/question_list.py +0 -22
- edsl/prompts/library/question_multiple_choice.py +0 -44
- edsl/prompts/library/question_numerical.py +0 -31
- edsl/prompts/library/question_rank.py +0 -21
- edsl/prompts/prompt_config.py +0 -33
- edsl/prompts/registry.py +0 -185
- edsl/questions/Question.py +0 -240
- edsl/report/InputOutputDataTypes.py +0 -134
- edsl/report/RegressionMixin.py +0 -28
- edsl/report/ReportOutputs.py +0 -1228
- edsl/report/ResultsFetchMixin.py +0 -106
- edsl/report/ResultsOutputMixin.py +0 -14
- edsl/report/demo.ipynb +0 -645
- edsl/results/ResultsDBMixin.py +0 -184
- edsl/surveys/SurveyFlowVisualizationMixin.py +0 -92
- edsl/trackers/Tracker.py +0 -91
- edsl/trackers/TrackerAPI.py +0 -196
- edsl/trackers/TrackerTasks.py +0 -70
- edsl/utilities/pastebin.py +0 -141
- edsl-0.1.15.dist-info/METADATA +0 -69
- edsl-0.1.15.dist-info/RECORD +0 -142
- /edsl/{language_models/model_interfaces → inference_services}/__init__.py +0 -0
- /edsl/{report/__init__.py → jobs/runners/JobsRunnerStatusData.py} +0 -0
- /edsl/{trackers/__init__.py → language_models/ServiceDataSources.py} +0 -0
- {edsl-0.1.15.dist-info → edsl-0.1.40.dist-info}/LICENSE +0 -0
@@ -0,0 +1,295 @@
|
|
1
|
+
from typing import Dict, List, Any, Optional, List
|
2
|
+
|
3
|
+
|
4
|
+
def is_hashable(v):
|
5
|
+
try:
|
6
|
+
hash(v)
|
7
|
+
return True
|
8
|
+
except TypeError:
|
9
|
+
return False
|
10
|
+
|
11
|
+
|
12
|
+
class TreeNode:
|
13
|
+
def __init__(self, key=None, value=None):
|
14
|
+
self.key = key
|
15
|
+
self.value = value
|
16
|
+
self.children = {}
|
17
|
+
|
18
|
+
|
19
|
+
class Tree:
|
20
|
+
def __init__(self, data: "Dataset", node_order: Optional[List[str]] = None):
|
21
|
+
d = {}
|
22
|
+
for entry in data:
|
23
|
+
d.update(entry)
|
24
|
+
self.data = d
|
25
|
+
self.root = None
|
26
|
+
|
27
|
+
self.node_order = node_order
|
28
|
+
|
29
|
+
self.construct_tree(node_order)
|
30
|
+
|
31
|
+
def unique_values_by_keys(self) -> dict:
|
32
|
+
unique_values = {}
|
33
|
+
for key, raw_values in self.data.items():
|
34
|
+
values = [v if is_hashable(v) else str(v) for v in raw_values]
|
35
|
+
unique_values[key] = list(set(values))
|
36
|
+
return unique_values
|
37
|
+
|
38
|
+
def construct_tree(self, node_order: Optional[List[str]] = None):
|
39
|
+
# Validate node_order
|
40
|
+
if node_order is None:
|
41
|
+
unique_values = self.unique_values_by_keys()
|
42
|
+
# Sort keys by number of unique values
|
43
|
+
node_order = sorted(
|
44
|
+
unique_values, key=lambda k: len(unique_values[k]), reverse=True
|
45
|
+
)
|
46
|
+
else:
|
47
|
+
if not set(node_order).issubset(set(self.data.keys())):
|
48
|
+
invalid_keys = set(node_order) - set(self.data.keys())
|
49
|
+
raise ValueError(f"Invalid keys in node_order: {invalid_keys}")
|
50
|
+
|
51
|
+
self.root = TreeNode()
|
52
|
+
|
53
|
+
for i in range(len(self.data[node_order[0]])):
|
54
|
+
current = self.root
|
55
|
+
for level in node_order[:-1]:
|
56
|
+
value = self.data[level][i]
|
57
|
+
if not is_hashable(value):
|
58
|
+
value = str(value)
|
59
|
+
if value not in current.children:
|
60
|
+
current.children[value] = TreeNode(key=level, value=value)
|
61
|
+
current = current.children[value]
|
62
|
+
|
63
|
+
leaf_key = node_order[-1]
|
64
|
+
leaf_value = self.data[leaf_key][i]
|
65
|
+
if not is_hashable(leaf_value):
|
66
|
+
leaf_value = str(leaf_value)
|
67
|
+
if leaf_value not in current.children:
|
68
|
+
current.children[leaf_value] = TreeNode(key=leaf_key, value=leaf_value)
|
69
|
+
|
70
|
+
def __repr__(self):
|
71
|
+
if self.node_order is not None:
|
72
|
+
return f"Tree(Dataset({self.data}), node_order={self.node_order})"
|
73
|
+
else:
|
74
|
+
return f"Tree(Dataset({self.data}))"
|
75
|
+
|
76
|
+
def print_tree(
|
77
|
+
self, node: Optional[TreeNode] = None, level: int = 0, print_keys: bool = False
|
78
|
+
):
|
79
|
+
if node is None:
|
80
|
+
node = self.root
|
81
|
+
if node is None:
|
82
|
+
print("Tree has not been constructed yet.")
|
83
|
+
return
|
84
|
+
|
85
|
+
if node.value is not None:
|
86
|
+
if print_keys and node.key is not None:
|
87
|
+
print(" " * level + f"{node.key}: {node.value}")
|
88
|
+
else:
|
89
|
+
print(" " * level + str(node.value))
|
90
|
+
for child in node.children.values():
|
91
|
+
self.print_tree(child, level + 1, print_keys)
|
92
|
+
|
93
|
+
def to_docx(self, filename: Optional[str] = None):
|
94
|
+
if filename is None:
|
95
|
+
filename = "tree_structure.docx"
|
96
|
+
|
97
|
+
from docx import Document
|
98
|
+
from docx.shared import Inches, Pt
|
99
|
+
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
100
|
+
from docx.enum.style import WD_STYLE_TYPE
|
101
|
+
|
102
|
+
doc = Document()
|
103
|
+
|
104
|
+
# Create styles for headings
|
105
|
+
for i in range(1, 10): # Up to 9 levels of headings
|
106
|
+
style_name = f"Heading {i}"
|
107
|
+
if style_name not in doc.styles:
|
108
|
+
doc.styles.add_style(style_name, WD_STYLE_TYPE.PARAGRAPH)
|
109
|
+
|
110
|
+
# Get or create the 'Body Text' style
|
111
|
+
if "Body Text" not in doc.styles:
|
112
|
+
body_style = doc.styles.add_style("Body Text", WD_STYLE_TYPE.PARAGRAPH)
|
113
|
+
else:
|
114
|
+
body_style = doc.styles["Body Text"]
|
115
|
+
|
116
|
+
body_style.font.size = Pt(11)
|
117
|
+
|
118
|
+
self._add_to_docx(doc, self.root, 0)
|
119
|
+
import base64
|
120
|
+
from io import BytesIO
|
121
|
+
import base64
|
122
|
+
|
123
|
+
# Save document to bytes buffer
|
124
|
+
doc_buffer = BytesIO()
|
125
|
+
doc.save(doc_buffer)
|
126
|
+
doc_buffer.seek(0)
|
127
|
+
|
128
|
+
base64_string = base64.b64encode(doc_buffer.getvalue()).decode("utf-8")
|
129
|
+
from edsl.scenarios.FileStore import FileStore
|
130
|
+
|
131
|
+
# Create and return FileStore instance
|
132
|
+
return FileStore(
|
133
|
+
path="tree_structure.docx", # Default name
|
134
|
+
mime_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
135
|
+
binary=True,
|
136
|
+
suffix="docx",
|
137
|
+
base64_string=base64_string,
|
138
|
+
)
|
139
|
+
# doc.save(filename)
|
140
|
+
# from edsl.utilities.utilities import file_notice
|
141
|
+
# file_notice(filename)
|
142
|
+
|
143
|
+
def _repr_html_(self):
|
144
|
+
"""Returns an interactive HTML representation of the tree with collapsible sections."""
|
145
|
+
|
146
|
+
# Generate a unique ID for this tree instance
|
147
|
+
import uuid
|
148
|
+
|
149
|
+
tree_id = f"tree_{uuid.uuid4().hex[:8]}"
|
150
|
+
|
151
|
+
styles = f"""
|
152
|
+
<div class="{tree_id}">
|
153
|
+
<style>
|
154
|
+
.{tree_id} details {{
|
155
|
+
margin-left: 20px;
|
156
|
+
}}
|
157
|
+
.{tree_id} summary {{
|
158
|
+
cursor: pointer;
|
159
|
+
margin: 2px 0;
|
160
|
+
}}
|
161
|
+
.{tree_id} .value {{
|
162
|
+
font-family: monospace;
|
163
|
+
background: #f5f5f5;
|
164
|
+
padding: 2px 6px;
|
165
|
+
border-radius: 3px;
|
166
|
+
margin: 1px 0;
|
167
|
+
}}
|
168
|
+
.{tree_id} .key {{
|
169
|
+
color: #666;
|
170
|
+
font-style: italic;
|
171
|
+
}}
|
172
|
+
</style>
|
173
|
+
"""
|
174
|
+
|
175
|
+
def node_to_html(node, level=0, print_keys=True):
|
176
|
+
if node is None:
|
177
|
+
return "Tree has not been constructed yet."
|
178
|
+
|
179
|
+
html = []
|
180
|
+
|
181
|
+
if node.value is not None:
|
182
|
+
# Create the node content
|
183
|
+
content = []
|
184
|
+
if print_keys and node.key is not None:
|
185
|
+
content.append(f'<span class="key">{node.key}: </span>')
|
186
|
+
content.append(f'<span class="value">{node.value}</span>')
|
187
|
+
content_html = "".join(content)
|
188
|
+
|
189
|
+
if node.children:
|
190
|
+
# Node with children
|
191
|
+
html.append(f'<details {"open" if level < 1 else ""}>')
|
192
|
+
html.append(f"<summary>{content_html}</summary>")
|
193
|
+
for child in node.children.values():
|
194
|
+
html.append(node_to_html(child, level + 1, print_keys))
|
195
|
+
html.append("</details>")
|
196
|
+
else:
|
197
|
+
# Leaf node
|
198
|
+
html.append(f"<div>{content_html}</div>")
|
199
|
+
else:
|
200
|
+
# Root node with no value
|
201
|
+
if node.children:
|
202
|
+
for child in node.children.values():
|
203
|
+
html.append(node_to_html(child, level, print_keys))
|
204
|
+
|
205
|
+
return "\n".join(html)
|
206
|
+
|
207
|
+
tree_html = node_to_html(self.root)
|
208
|
+
return f"{styles}{tree_html}</div>"
|
209
|
+
|
210
|
+
# def _repr_html_(self):
|
211
|
+
# """Returns an HTML representation of the tree, following the same logic as print_tree."""
|
212
|
+
# styles = """
|
213
|
+
# <style>
|
214
|
+
# .tree-container {
|
215
|
+
# font-family: monospace;
|
216
|
+
# white-space: pre;
|
217
|
+
# margin: 10px;
|
218
|
+
# }
|
219
|
+
# </style>
|
220
|
+
# """
|
221
|
+
|
222
|
+
# def node_to_html(node, level=0, print_keys=False):
|
223
|
+
# if node is None:
|
224
|
+
# node = self.root
|
225
|
+
# if node is None:
|
226
|
+
# return "Tree has not been constructed yet."
|
227
|
+
|
228
|
+
# html = []
|
229
|
+
# if node.value is not None:
|
230
|
+
# indent = " " * 2 * level # Using for HTML spaces
|
231
|
+
# if print_keys and node.key is not None:
|
232
|
+
# html.append(f"{indent}{node.key}: {node.value}<br>")
|
233
|
+
# else:
|
234
|
+
# html.append(f"{indent}{node.value}<br>")
|
235
|
+
|
236
|
+
# for child in node.children.values():
|
237
|
+
# html.append(node_to_html(child, level + 1, print_keys))
|
238
|
+
|
239
|
+
# return "".join(html)
|
240
|
+
|
241
|
+
# tree_html = node_to_html(self.root)
|
242
|
+
# return f'<div class="tree-container">{tree_html}</div>{styles}'
|
243
|
+
|
244
|
+
def _add_to_docx(self, doc, node: TreeNode, level: int):
|
245
|
+
if node.value is not None:
|
246
|
+
if level == 0:
|
247
|
+
doc.add_heading(str(node.value), level=level + 1)
|
248
|
+
elif node.children: # If the node has children, it's not the last level
|
249
|
+
para = doc.add_paragraph(str(node.value))
|
250
|
+
para.style = f"Heading {level+1}"
|
251
|
+
else: # If the node has no children, it's the last level (body text)
|
252
|
+
para = doc.add_paragraph(str(node.value))
|
253
|
+
para.style = "Body Text"
|
254
|
+
|
255
|
+
# Process child nodes (moved outside the if block)
|
256
|
+
for child in node.children.values():
|
257
|
+
self._add_to_docx(doc, child, level + 1)
|
258
|
+
|
259
|
+
|
260
|
+
# Example usage (commented out)
|
261
|
+
"""
|
262
|
+
from edsl.results.Dataset import Dataset
|
263
|
+
|
264
|
+
data = Dataset(
|
265
|
+
[
|
266
|
+
{"continent": ["North America", "Asia", "Europe", "North America", "Asia"]},
|
267
|
+
{"country": ["US", "China", "France", "Canada", "Japan"]},
|
268
|
+
{"city": ["New York", "Beijing", "Paris", "Toronto", "Tokyo"]},
|
269
|
+
{"population": [8419000, 21540000, 2161000, 2930000, 13960000]},
|
270
|
+
]
|
271
|
+
)
|
272
|
+
|
273
|
+
tree = Tree(data)
|
274
|
+
|
275
|
+
try:
|
276
|
+
tree.construct_tree(["continent", "country", "city", "population"])
|
277
|
+
print("Tree without key names:")
|
278
|
+
tree.print_tree()
|
279
|
+
print("\nTree with key names:")
|
280
|
+
tree.print_tree(print_keys=True)
|
281
|
+
except ValueError as e:
|
282
|
+
print(f"Error: {e}")
|
283
|
+
|
284
|
+
# Demonstrating validation
|
285
|
+
try:
|
286
|
+
tree.construct_tree(["continent", "country", "invalid_key"])
|
287
|
+
except ValueError as e:
|
288
|
+
print(f"\nValidation Error: {e}")
|
289
|
+
|
290
|
+
tree = Tree(data)
|
291
|
+
tree.construct_tree(["continent", "country", "city", "population"])
|
292
|
+
tree.print_tree(print_keys=True)
|
293
|
+
tree.to_docx("tree_structure.docx")
|
294
|
+
print("DocX file 'tree_structure.docx' has been created.")
|
295
|
+
"""
|
@@ -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
|