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,45 @@
|
|
1
|
+
"""Mixin class for exporting results."""
|
2
|
+
|
3
|
+
from functools import wraps
|
4
|
+
from edsl.results.DatasetExportMixin import DatasetExportMixin
|
5
|
+
|
6
|
+
|
7
|
+
def to_dataset(func):
|
8
|
+
"""Convert the object to a Dataset object before calling the function."""
|
9
|
+
|
10
|
+
@wraps(func)
|
11
|
+
def wrapper(self, *args, **kwargs):
|
12
|
+
"""Return the function with the Results object converted to a Dataset object."""
|
13
|
+
if self.__class__.__name__ == "ScenarioList":
|
14
|
+
return func(self.to_dataset(), *args, **kwargs)
|
15
|
+
else:
|
16
|
+
raise Exception(
|
17
|
+
f"Class {self.__class__.__name__} not recognized as a Results or Dataset object."
|
18
|
+
)
|
19
|
+
|
20
|
+
return wrapper
|
21
|
+
|
22
|
+
|
23
|
+
def decorate_methods_from_mixin(cls, mixin_cls):
|
24
|
+
for attr_name, attr_value in mixin_cls.__dict__.items():
|
25
|
+
if callable(attr_value) and not attr_name.startswith("__"):
|
26
|
+
setattr(cls, attr_name, to_dataset(attr_value))
|
27
|
+
return cls
|
28
|
+
|
29
|
+
|
30
|
+
# @decorate_all_methods
|
31
|
+
class ScenarioListExportMixin(DatasetExportMixin):
|
32
|
+
"""Mixin class for exporting Results objects."""
|
33
|
+
|
34
|
+
def __init_subclass__(cls, **kwargs):
|
35
|
+
super().__init_subclass__(**kwargs)
|
36
|
+
decorate_methods_from_mixin(cls, DatasetExportMixin)
|
37
|
+
|
38
|
+
def to_docx(self, filename: str):
|
39
|
+
"""Export the ScenarioList to a .docx file."""
|
40
|
+
dataset = self.to_dataset()
|
41
|
+
from edsl.results.DatasetTree import Tree
|
42
|
+
|
43
|
+
tree = Tree(dataset)
|
44
|
+
tree.construct_tree()
|
45
|
+
tree.to_docx(filename)
|
@@ -0,0 +1,239 @@
|
|
1
|
+
import os
|
2
|
+
import re
|
3
|
+
import copy
|
4
|
+
import atexit
|
5
|
+
import tempfile
|
6
|
+
import subprocess
|
7
|
+
|
8
|
+
|
9
|
+
class GoogleDriveDownloader:
|
10
|
+
_temp_dir = None
|
11
|
+
_temp_file_path = None
|
12
|
+
|
13
|
+
@classmethod
|
14
|
+
def fetch_from_drive(cls, url, filename=None):
|
15
|
+
import requests
|
16
|
+
|
17
|
+
# Extract file ID from the URL
|
18
|
+
file_id = cls._extract_file_id(url)
|
19
|
+
if not file_id:
|
20
|
+
raise ValueError("Invalid Google Drive URL")
|
21
|
+
|
22
|
+
# Construct the download URL
|
23
|
+
download_url = f"https://drive.google.com/uc?export=download&id={file_id}"
|
24
|
+
|
25
|
+
# Send a GET request to the URL
|
26
|
+
session = requests.Session()
|
27
|
+
response = session.get(download_url, stream=True)
|
28
|
+
response.raise_for_status()
|
29
|
+
|
30
|
+
# Check for large file download prompt
|
31
|
+
for key, value in response.cookies.items():
|
32
|
+
if key.startswith("download_warning"):
|
33
|
+
params = {"id": file_id, "confirm": value}
|
34
|
+
response = session.get(download_url, params=params, stream=True)
|
35
|
+
break
|
36
|
+
|
37
|
+
# Create a temporary file to save the download
|
38
|
+
if not filename:
|
39
|
+
filename = "downloaded_file"
|
40
|
+
|
41
|
+
if cls._temp_dir is None:
|
42
|
+
cls._temp_dir = tempfile.TemporaryDirectory()
|
43
|
+
atexit.register(cls._cleanup)
|
44
|
+
|
45
|
+
cls._temp_file_path = os.path.join(cls._temp_dir.name, filename)
|
46
|
+
|
47
|
+
# Write the content to the temporary file
|
48
|
+
with open(cls._temp_file_path, "wb") as f:
|
49
|
+
for chunk in response.iter_content(32768):
|
50
|
+
if chunk:
|
51
|
+
f.write(chunk)
|
52
|
+
|
53
|
+
print(f"File saved to: {cls._temp_file_path}")
|
54
|
+
|
55
|
+
return cls._temp_file_path
|
56
|
+
|
57
|
+
@staticmethod
|
58
|
+
def _extract_file_id(url):
|
59
|
+
from urllib.parse import urlparse, parse_qs
|
60
|
+
|
61
|
+
# Try to extract file ID from '/file/d/' format
|
62
|
+
file_id_match = re.search(r"/d/([a-zA-Z0-9-_]+)", url)
|
63
|
+
if file_id_match:
|
64
|
+
return file_id_match.group(1)
|
65
|
+
|
66
|
+
# If not found, try to extract from 'open?id=' format
|
67
|
+
parsed_url = urlparse(url)
|
68
|
+
query_params = parse_qs(parsed_url.query)
|
69
|
+
if "id" in query_params:
|
70
|
+
return query_params["id"][0]
|
71
|
+
|
72
|
+
return None
|
73
|
+
|
74
|
+
@classmethod
|
75
|
+
def _cleanup(cls):
|
76
|
+
if cls._temp_dir:
|
77
|
+
cls._temp_dir.cleanup()
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def get_temp_file_path(cls):
|
81
|
+
return cls._temp_file_path
|
82
|
+
|
83
|
+
|
84
|
+
def fetch_and_save_pdf(url, filename):
|
85
|
+
# Send a GET request to the URL
|
86
|
+
import requests
|
87
|
+
|
88
|
+
response = requests.get(url)
|
89
|
+
|
90
|
+
# Check if the request was successful
|
91
|
+
response.raise_for_status()
|
92
|
+
|
93
|
+
# Create a temporary directory
|
94
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
95
|
+
# Construct the full path for the file
|
96
|
+
temp_file_path = os.path.join(temp_dir, filename)
|
97
|
+
|
98
|
+
# Write the content to the temporary file
|
99
|
+
with open(temp_file_path, "wb") as file:
|
100
|
+
file.write(response.content)
|
101
|
+
|
102
|
+
print(f"PDF saved to: {temp_file_path}")
|
103
|
+
|
104
|
+
# Here you can perform operations with the file
|
105
|
+
# The file will be automatically deleted when you exit this block
|
106
|
+
|
107
|
+
return temp_file_path
|
108
|
+
|
109
|
+
|
110
|
+
class ScenarioListPdfMixin:
|
111
|
+
@classmethod
|
112
|
+
def from_pdf(cls, filename_or_url, collapse_pages=False):
|
113
|
+
# Check if the input is a URL
|
114
|
+
if cls.is_url(filename_or_url):
|
115
|
+
# Check if it's a Google Drive URL
|
116
|
+
if "drive.google.com" in filename_or_url:
|
117
|
+
temp_filename = GoogleDriveDownloader.fetch_from_drive(
|
118
|
+
filename_or_url, "temp_pdf.pdf"
|
119
|
+
)
|
120
|
+
else:
|
121
|
+
# For other URLs, use the previous fetch_and_save_pdf function
|
122
|
+
temp_filename = fetch_and_save_pdf(filename_or_url, "temp_pdf.pdf")
|
123
|
+
|
124
|
+
scenarios = list(cls.extract_text_from_pdf(temp_filename))
|
125
|
+
else:
|
126
|
+
# If it's not a URL, assume it's a local file path
|
127
|
+
scenarios = list(cls.extract_text_from_pdf(filename_or_url))
|
128
|
+
if not collapse_pages:
|
129
|
+
return cls(scenarios)
|
130
|
+
else:
|
131
|
+
txt = ""
|
132
|
+
for scenario in scenarios:
|
133
|
+
txt += scenario["text"]
|
134
|
+
from edsl.scenarios import Scenario
|
135
|
+
|
136
|
+
base_scenario = copy.copy(scenarios[0])
|
137
|
+
base_scenario["text"] = txt
|
138
|
+
return base_scenario
|
139
|
+
|
140
|
+
@staticmethod
|
141
|
+
def is_url(string):
|
142
|
+
from urllib.parse import urlparse
|
143
|
+
|
144
|
+
try:
|
145
|
+
result = urlparse(string)
|
146
|
+
return all([result.scheme, result.netloc])
|
147
|
+
except ValueError:
|
148
|
+
return False
|
149
|
+
|
150
|
+
@classmethod
|
151
|
+
def _from_pdf_to_image(cls, pdf_path, image_format="jpeg"):
|
152
|
+
"""
|
153
|
+
Convert each page of a PDF into an image and create Scenario instances.
|
154
|
+
|
155
|
+
:param pdf_path: Path to the PDF file.
|
156
|
+
:param image_format: Format of the output images (default is 'jpeg').
|
157
|
+
:return: ScenarioList instance containing the Scenario instances.
|
158
|
+
"""
|
159
|
+
import tempfile
|
160
|
+
from pdf2image import convert_from_path
|
161
|
+
from edsl.scenarios import Scenario
|
162
|
+
|
163
|
+
with tempfile.TemporaryDirectory() as output_folder:
|
164
|
+
# Convert PDF to images
|
165
|
+
images = convert_from_path(pdf_path)
|
166
|
+
|
167
|
+
scenarios = []
|
168
|
+
|
169
|
+
# Save each page as an image and create Scenario instances
|
170
|
+
for i, image in enumerate(images):
|
171
|
+
image_path = os.path.join(output_folder, f"page_{i+1}.{image_format}")
|
172
|
+
image.save(image_path, image_format.upper())
|
173
|
+
|
174
|
+
scenario = Scenario._from_filepath_image(image_path)
|
175
|
+
scenarios.append(scenario)
|
176
|
+
|
177
|
+
# print(f"Saved {len(images)} pages as images in {output_folder}")
|
178
|
+
return cls(scenarios)
|
179
|
+
|
180
|
+
@staticmethod
|
181
|
+
def extract_text_from_pdf(pdf_path):
|
182
|
+
from edsl.scenarios.Scenario import Scenario
|
183
|
+
import fitz # PyMuPDF
|
184
|
+
|
185
|
+
# TODO: Add test case
|
186
|
+
# Ensure the file exists
|
187
|
+
if not os.path.exists(pdf_path):
|
188
|
+
raise FileNotFoundError(f"The file {pdf_path} does not exist.")
|
189
|
+
|
190
|
+
# Open the PDF file
|
191
|
+
document = fitz.open(pdf_path)
|
192
|
+
|
193
|
+
# Get the filename from the path
|
194
|
+
filename = os.path.basename(pdf_path)
|
195
|
+
|
196
|
+
# Iterate through each page and extract text
|
197
|
+
for page_num in range(len(document)):
|
198
|
+
page = document.load_page(page_num)
|
199
|
+
text = page.get_text()
|
200
|
+
|
201
|
+
# Create a dictionary for the current page
|
202
|
+
page_info = {"filename": filename, "page": page_num + 1, "text": text}
|
203
|
+
yield Scenario(page_info)
|
204
|
+
|
205
|
+
def create_hello_world_pdf(pdf_path):
|
206
|
+
# LaTeX content
|
207
|
+
latex_content = r"""
|
208
|
+
\documentclass{article}
|
209
|
+
\title{Hello World}
|
210
|
+
\author{John}
|
211
|
+
\date{\today}
|
212
|
+
\begin{document}
|
213
|
+
\maketitle
|
214
|
+
\section{Hello, World!}
|
215
|
+
This is a simple hello world example created with LaTeX and Python.
|
216
|
+
\end{document}
|
217
|
+
"""
|
218
|
+
|
219
|
+
# Create a .tex file
|
220
|
+
tex_filename = pdf_path + ".tex"
|
221
|
+
with open(tex_filename, "w") as tex_file:
|
222
|
+
tex_file.write(latex_content)
|
223
|
+
|
224
|
+
# Compile the .tex file to PDF
|
225
|
+
subprocess.run(["pdflatex", tex_filename], check=True)
|
226
|
+
|
227
|
+
# Optionally, clean up auxiliary files generated by pdflatex
|
228
|
+
aux_files = [pdf_path + ext for ext in [".aux", ".log"]]
|
229
|
+
for aux_file in aux_files:
|
230
|
+
try:
|
231
|
+
os.remove(aux_file)
|
232
|
+
except FileNotFoundError:
|
233
|
+
pass
|
234
|
+
|
235
|
+
|
236
|
+
if __name__ == "__main__":
|
237
|
+
import doctest
|
238
|
+
|
239
|
+
doctest.testmod()
|
edsl/scenarios/__init__.py
CHANGED
@@ -0,0 +1,96 @@
|
|
1
|
+
# directory_scanner.py
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Optional, List, Iterator, TypeVar, Generic, Callable, Any
|
4
|
+
import os
|
5
|
+
|
6
|
+
T = TypeVar("T")
|
7
|
+
|
8
|
+
|
9
|
+
@dataclass
|
10
|
+
class DirectoryScanner:
|
11
|
+
"""
|
12
|
+
Scanner for finding files in a directory based on various criteria.
|
13
|
+
"""
|
14
|
+
|
15
|
+
directory_path: str
|
16
|
+
|
17
|
+
def scan(
|
18
|
+
self,
|
19
|
+
factory: Callable[[str], T],
|
20
|
+
recursive: bool = False,
|
21
|
+
suffix_allow_list: Optional[List[str]] = None,
|
22
|
+
suffix_exclude_list: Optional[List[str]] = None,
|
23
|
+
example_suffix: Optional[str] = None,
|
24
|
+
include_no_extension: bool = True,
|
25
|
+
) -> List[T]:
|
26
|
+
"""
|
27
|
+
Eagerly scan directory and return list of objects created by factory.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
factory: Callable that creates objects from file paths
|
31
|
+
recursive: If True, recursively traverse subdirectories
|
32
|
+
suffix_allow_list: List of allowed file extensions (without dots)
|
33
|
+
suffix_exclude_list: List of excluded file extensions (takes precedence over allow list)
|
34
|
+
example_suffix: If provided, only include files with this example suffix
|
35
|
+
include_no_extension: Whether to include files without extensions
|
36
|
+
"""
|
37
|
+
return list(
|
38
|
+
self.iter_scan(
|
39
|
+
factory,
|
40
|
+
recursive=recursive,
|
41
|
+
suffix_allow_list=suffix_allow_list,
|
42
|
+
suffix_exclude_list=suffix_exclude_list,
|
43
|
+
example_suffix=example_suffix,
|
44
|
+
include_no_extension=include_no_extension,
|
45
|
+
)
|
46
|
+
)
|
47
|
+
|
48
|
+
def iter_scan(
|
49
|
+
self,
|
50
|
+
factory: Callable[[str], T],
|
51
|
+
recursive: bool = False,
|
52
|
+
suffix_allow_list: Optional[List[str]] = None,
|
53
|
+
suffix_exclude_list: Optional[List[str]] = None,
|
54
|
+
example_suffix: Optional[str] = None,
|
55
|
+
include_no_extension: bool = True,
|
56
|
+
) -> Iterator[T]:
|
57
|
+
"""
|
58
|
+
Lazily scan directory and yield objects created by factory.
|
59
|
+
"""
|
60
|
+
|
61
|
+
def should_include_file(filepath: str) -> bool:
|
62
|
+
_, ext = os.path.splitext(filepath)
|
63
|
+
ext = ext[1:] if ext else ""
|
64
|
+
|
65
|
+
# Handle no extension case
|
66
|
+
if not ext:
|
67
|
+
return include_no_extension
|
68
|
+
|
69
|
+
# Check exclusions first (they take precedence)
|
70
|
+
if suffix_exclude_list and ext in suffix_exclude_list:
|
71
|
+
return False
|
72
|
+
|
73
|
+
# Check example suffix if specified
|
74
|
+
if example_suffix and not filepath.endswith(example_suffix):
|
75
|
+
return False
|
76
|
+
|
77
|
+
# Check allowed suffixes if specified
|
78
|
+
if suffix_allow_list and ext not in suffix_allow_list:
|
79
|
+
return False
|
80
|
+
|
81
|
+
return True
|
82
|
+
|
83
|
+
def iter_files():
|
84
|
+
if recursive:
|
85
|
+
for root, _, files in os.walk(self.directory_path):
|
86
|
+
for file in files:
|
87
|
+
yield os.path.join(root, file)
|
88
|
+
else:
|
89
|
+
for file in os.listdir(self.directory_path):
|
90
|
+
file_path = os.path.join(self.directory_path, file)
|
91
|
+
if os.path.isfile(file_path):
|
92
|
+
yield file_path
|
93
|
+
|
94
|
+
for file_path in iter_files():
|
95
|
+
if should_include_file(file_path):
|
96
|
+
yield factory(file_path)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
from typing import Optional, Dict, Type
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
import importlib.metadata
|
4
|
+
import importlib.util
|
5
|
+
|
6
|
+
from edsl.utilities.is_notebook import is_notebook
|
7
|
+
|
8
|
+
|
9
|
+
class FileMethods(ABC):
|
10
|
+
_handlers: Dict[str, Type["FileMethods"]] = {}
|
11
|
+
|
12
|
+
def __init__(self, path: Optional[str] = None):
|
13
|
+
self.path = path
|
14
|
+
|
15
|
+
def __init_subclass__(cls) -> None:
|
16
|
+
"""Register subclasses automatically when they're defined."""
|
17
|
+
super().__init_subclass__()
|
18
|
+
if hasattr(cls, "suffix"):
|
19
|
+
FileMethods._handlers[cls.suffix] = cls
|
20
|
+
|
21
|
+
@classmethod
|
22
|
+
def get_handler(cls, suffix: str) -> Optional[Type["FileMethods"]]:
|
23
|
+
"""Get the appropriate handler class for a given suffix."""
|
24
|
+
# Load plugins if they haven't been loaded yet
|
25
|
+
if not cls._handlers:
|
26
|
+
cls.load_plugins()
|
27
|
+
return cls._handlers.get(suffix.lower())
|
28
|
+
|
29
|
+
@classmethod
|
30
|
+
def load_plugins(cls):
|
31
|
+
"""Load all file handler plugins including built-ins and external plugins."""
|
32
|
+
|
33
|
+
from edsl.scenarios import handlers
|
34
|
+
|
35
|
+
# Then load any external plugins
|
36
|
+
try:
|
37
|
+
entries = importlib.metadata.entry_points(group="file_handlers")
|
38
|
+
except TypeError: # some Python 3.9 bullshit
|
39
|
+
# entries = importlib.metadata.entry_points()
|
40
|
+
entries = []
|
41
|
+
|
42
|
+
for ep in entries:
|
43
|
+
try:
|
44
|
+
handler_class = ep.load()
|
45
|
+
# Registration happens automatically via __init_subclass__
|
46
|
+
except Exception as e:
|
47
|
+
print(f"Failed to load external handler {ep.name}: {e}")
|
48
|
+
|
49
|
+
@classmethod
|
50
|
+
def get_handler_for_path(cls, path: str) -> Optional[Type["FileMethods"]]:
|
51
|
+
"""Get the appropriate handler class for a file path."""
|
52
|
+
suffix = path.split(".")[-1].lower() if "." in path else ""
|
53
|
+
return cls.get_handler(suffix)
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
def create(cls, path: str) -> Optional["FileMethods"]:
|
57
|
+
"""Create an appropriate handler instance for the given path."""
|
58
|
+
handler_class = cls.get_handler_for_path(path)
|
59
|
+
if handler_class:
|
60
|
+
return handler_class(path)
|
61
|
+
return None
|
62
|
+
|
63
|
+
@classmethod
|
64
|
+
def supported_file_types(cls):
|
65
|
+
if not cls._handlers:
|
66
|
+
cls.load_plugins()
|
67
|
+
return list(cls._handlers.keys())
|
68
|
+
|
69
|
+
@abstractmethod
|
70
|
+
def view_system(self):
|
71
|
+
...
|
72
|
+
|
73
|
+
@abstractmethod
|
74
|
+
def view_notebook(self):
|
75
|
+
...
|
76
|
+
|
77
|
+
def view(self):
|
78
|
+
if is_notebook():
|
79
|
+
self.view_notebook()
|
80
|
+
else:
|
81
|
+
self.view_system()
|
82
|
+
|
83
|
+
@abstractmethod
|
84
|
+
def example(self):
|
85
|
+
...
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from .pdf import PdfMethods
|
2
|
+
from .docx import DocxMethods
|
3
|
+
from .png import PngMethods
|
4
|
+
from .txt import TxtMethods
|
5
|
+
from .html import HtmlMethods
|
6
|
+
from .md import MarkdownMethods
|
7
|
+
from .csv import CsvMethods
|
8
|
+
from .json import JsonMethods
|
9
|
+
from .sql import SqlMethods
|
10
|
+
from .pptx import PptxMethods
|
11
|
+
from .latex import LaTeXMethods
|
12
|
+
from .py import PyMethods
|
13
|
+
from .sqlite import SQLiteMethods
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import tempfile
|
2
|
+
from edsl.scenarios.file_methods import FileMethods
|
3
|
+
|
4
|
+
|
5
|
+
class CsvMethods(FileMethods):
|
6
|
+
suffix = "csv"
|
7
|
+
|
8
|
+
def view_system(self):
|
9
|
+
import os
|
10
|
+
import subprocess
|
11
|
+
|
12
|
+
if os.path.exists(self.path):
|
13
|
+
try:
|
14
|
+
if (os_name := os.name) == "posix":
|
15
|
+
subprocess.run(["open", self.path], check=True) # macOS
|
16
|
+
elif os_name == "nt":
|
17
|
+
os.startfile(self.path) # Windows
|
18
|
+
else:
|
19
|
+
subprocess.run(["xdg-open", self.path], check=True) # Linux
|
20
|
+
except Exception as e:
|
21
|
+
print(f"Error opening CSV: {e}")
|
22
|
+
else:
|
23
|
+
print("CSV file was not found.")
|
24
|
+
|
25
|
+
def view_notebook(self):
|
26
|
+
import pandas as pd
|
27
|
+
from IPython.display import display
|
28
|
+
|
29
|
+
df = pd.read_csv(self.path)
|
30
|
+
display(df)
|
31
|
+
|
32
|
+
def example(self):
|
33
|
+
import pandas as pd
|
34
|
+
|
35
|
+
df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
|
36
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".csv") as f:
|
37
|
+
df.to_csv(f.name, index=False)
|
38
|
+
return f.name
|
39
|
+
|
40
|
+
def to_pandas(self):
|
41
|
+
"""
|
42
|
+
Convert the CSV file to a pandas DataFrame.
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
pandas.DataFrame: The data from the CSV as a DataFrame
|
46
|
+
"""
|
47
|
+
import pandas as pd
|
48
|
+
|
49
|
+
return pd.read_csv(self.path)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
from edsl.scenarios.file_methods import FileMethods
|
2
|
+
import os
|
3
|
+
import tempfile
|
4
|
+
|
5
|
+
|
6
|
+
class DocxMethods(FileMethods):
|
7
|
+
suffix = "docx"
|
8
|
+
|
9
|
+
def extract_text(self):
|
10
|
+
from docx import Document
|
11
|
+
|
12
|
+
self.doc = Document(self.path)
|
13
|
+
|
14
|
+
# Extract all text
|
15
|
+
full_text = []
|
16
|
+
for para in self.doc.paragraphs:
|
17
|
+
full_text.append(para.text)
|
18
|
+
|
19
|
+
text = "\n".join(full_text)
|
20
|
+
return text
|
21
|
+
|
22
|
+
def view_system(self):
|
23
|
+
import os
|
24
|
+
import subprocess
|
25
|
+
|
26
|
+
if os.path.exists(self.path):
|
27
|
+
try:
|
28
|
+
if (os_name := os.name) == "posix":
|
29
|
+
subprocess.run(["open", self.path], check=True) # macOS
|
30
|
+
elif os_name == "nt":
|
31
|
+
os.startfile(self.path) # Windows
|
32
|
+
else:
|
33
|
+
subprocess.run(["xdg-open", self.path], check=True) # Linux
|
34
|
+
except Exception as e:
|
35
|
+
print(f"Error opening DOCX: {e}")
|
36
|
+
else:
|
37
|
+
print("DOCX file was not found.")
|
38
|
+
|
39
|
+
def view_notebook(self):
|
40
|
+
import mammoth
|
41
|
+
from IPython.display import HTML, display
|
42
|
+
|
43
|
+
with open(self.path, "rb") as docx_file:
|
44
|
+
result = mammoth.convert_to_html(docx_file)
|
45
|
+
html = f"""
|
46
|
+
<div style="width: 800px; height: 800px; padding: 20px;
|
47
|
+
border: 1px solid #ccc; overflow-y: auto;">
|
48
|
+
{result.value}
|
49
|
+
</div>
|
50
|
+
"""
|
51
|
+
display(HTML(html))
|
52
|
+
|
53
|
+
def example(self):
|
54
|
+
from docx import Document
|
55
|
+
from edsl.scenarios.Scenario import Scenario
|
56
|
+
from edsl.scenarios.ScenarioList import ScenarioList
|
57
|
+
|
58
|
+
os.makedirs("test_dir", exist_ok=True)
|
59
|
+
doc1 = Document()
|
60
|
+
_ = doc1.add_heading("First Survey")
|
61
|
+
doc1.save("test_dir/test1.docx")
|
62
|
+
doc2 = Document()
|
63
|
+
_ = doc2.add_heading("Second Survey")
|
64
|
+
|
65
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".docx") as tmp:
|
66
|
+
doc2.save(tmp.name)
|
67
|
+
tmp.close()
|
68
|
+
|
69
|
+
return tmp.name
|
70
|
+
|
71
|
+
|
72
|
+
if __name__ == "__main__":
|
73
|
+
docx_temp = DocxMethods.example()
|
74
|
+
from edsl.scenarios.FileStore import FileStore
|
75
|
+
|
76
|
+
fs = FileStore(docx_temp)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from edsl.scenarios.file_methods import FileMethods
|
2
|
+
import tempfile
|
3
|
+
|
4
|
+
|
5
|
+
class HtmlMethods(FileMethods):
|
6
|
+
suffix = "html"
|
7
|
+
|
8
|
+
def view_system(self):
|
9
|
+
import webbrowser
|
10
|
+
|
11
|
+
# with open(self.path, "r") as f:
|
12
|
+
# html_string = f.read()
|
13
|
+
|
14
|
+
# html_path = self.to_tempfile()
|
15
|
+
# webbrowser.open("file://" + html_path)
|
16
|
+
webbrowser.open("file://" + self.path)
|
17
|
+
|
18
|
+
def view_notebook(self):
|
19
|
+
from IPython.display import IFrame, display
|
20
|
+
|
21
|
+
display(IFrame(self.path, width=800, height=800))
|
22
|
+
|
23
|
+
def example(self):
|
24
|
+
html_string = b"""
|
25
|
+
<html>
|
26
|
+
<head>
|
27
|
+
<title>Test</title>
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<h1>Hello, World!</h1>
|
31
|
+
</body>
|
32
|
+
</html>
|
33
|
+
"""
|
34
|
+
|
35
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".html") as f:
|
36
|
+
f.write(html_string)
|
37
|
+
return f.name
|