edsl 0.1.39.dev2__py3-none-any.whl → 0.1.39.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edsl/Base.py +332 -385
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +49 -57
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +867 -1079
- edsl/agents/AgentList.py +413 -551
- edsl/agents/Invigilator.py +233 -285
- edsl/agents/InvigilatorBase.py +270 -254
- edsl/agents/PromptConstructor.py +354 -252
- edsl/agents/__init__.py +3 -2
- edsl/agents/descriptors.py +99 -99
- edsl/agents/prompt_helpers.py +129 -129
- edsl/auto/AutoStudy.py +117 -117
- edsl/auto/StageBase.py +230 -230
- edsl/auto/StageGenerateSurvey.py +178 -178
- edsl/auto/StageLabelQuestions.py +125 -125
- edsl/auto/StagePersona.py +61 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
- edsl/auto/StagePersonaDimensionValues.py +74 -74
- edsl/auto/StagePersonaDimensions.py +69 -69
- edsl/auto/StageQuestions.py +73 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +224 -224
- edsl/base/Base.py +279 -279
- edsl/config.py +157 -177
- edsl/conversation/Conversation.py +290 -290
- edsl/conversation/car_buying.py +58 -59
- edsl/conversation/chips.py +95 -95
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +1028 -1090
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +555 -562
- edsl/data/CacheEntry.py +233 -230
- edsl/data/CacheHandler.py +149 -170
- edsl/data/RemoteCacheSync.py +78 -78
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +4 -5
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +73 -74
- edsl/enums.py +175 -195
- edsl/exceptions/BaseException.py +21 -21
- edsl/exceptions/__init__.py +54 -54
- edsl/exceptions/agents.py +42 -54
- edsl/exceptions/cache.py +5 -5
- edsl/exceptions/configuration.py +16 -16
- edsl/exceptions/coop.py +10 -10
- edsl/exceptions/data.py +14 -14
- edsl/exceptions/general.py +34 -34
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +91 -109
- edsl/exceptions/results.py +29 -29
- edsl/exceptions/scenarios.py +22 -29
- edsl/exceptions/surveys.py +37 -37
- edsl/inference_services/AnthropicService.py +87 -84
- edsl/inference_services/AwsBedrock.py +120 -118
- edsl/inference_services/AzureAI.py +217 -215
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +148 -139
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +147 -80
- edsl/inference_services/InferenceServicesCollection.py +97 -122
- edsl/inference_services/MistralAIService.py +123 -120
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +224 -221
- edsl/inference_services/PerplexityService.py +163 -160
- edsl/inference_services/TestService.py +89 -92
- edsl/inference_services/TogetherAIService.py +170 -170
- edsl/inference_services/models_available_cache.py +118 -118
- edsl/inference_services/rate_limits_cache.py +25 -25
- edsl/inference_services/registry.py +41 -41
- edsl/inference_services/write_available.py +10 -10
- edsl/jobs/Answers.py +56 -43
- edsl/jobs/Jobs.py +898 -757
- edsl/jobs/JobsChecks.py +147 -172
- edsl/jobs/JobsPrompts.py +268 -270
- edsl/jobs/JobsRemoteInferenceHandler.py +239 -287
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/buckets/BucketCollection.py +63 -104
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +251 -283
- edsl/jobs/interviews/Interview.py +661 -358
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
- edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
- edsl/jobs/interviews/InterviewStatistic.py +63 -63
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
- edsl/jobs/interviews/InterviewStatusLog.py +92 -92
- edsl/jobs/interviews/ReportErrors.py +66 -66
- edsl/jobs/interviews/interview_status_enum.py +9 -9
- edsl/jobs/runners/JobsRunnerAsyncio.py +466 -421
- edsl/jobs/runners/JobsRunnerStatus.py +330 -330
- edsl/jobs/tasks/QuestionTaskCreator.py +242 -244
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +450 -449
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +163 -161
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/KeyLookup.py +30 -0
- edsl/language_models/LanguageModel.py +668 -571
- edsl/language_models/ModelList.py +155 -153
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/__init__.py +3 -2
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- edsl/language_models/registry.py +190 -180
- edsl/language_models/repair.py +156 -156
- edsl/language_models/unused/ReplicateBase.py +83 -0
- edsl/language_models/utilities.py +64 -65
- edsl/notebooks/Notebook.py +258 -263
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +362 -352
- edsl/prompts/__init__.py +2 -2
- edsl/questions/AnswerValidatorMixin.py +289 -334
- edsl/questions/QuestionBase.py +664 -509
- edsl/questions/QuestionBaseGenMixin.py +161 -165
- edsl/questions/QuestionBasePromptsMixin.py +217 -221
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +182 -182
- edsl/questions/QuestionFreeText.py +114 -113
- edsl/questions/QuestionFunctional.py +166 -166
- edsl/questions/QuestionList.py +231 -229
- edsl/questions/QuestionMultipleChoice.py +286 -330
- edsl/questions/QuestionNumerical.py +153 -151
- edsl/questions/QuestionRank.py +324 -314
- edsl/questions/Quick.py +41 -41
- edsl/questions/RegisterQuestionsMeta.py +71 -71
- edsl/questions/ResponseValidatorABC.py +174 -200
- edsl/questions/SimpleAskMixin.py +73 -74
- edsl/questions/__init__.py +26 -27
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +87 -90
- edsl/questions/derived/QuestionTopK.py +93 -93
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +413 -427
- edsl/questions/prompt_templates/question_budget.jinja +13 -13
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
- edsl/questions/prompt_templates/question_extract.jinja +11 -11
- edsl/questions/prompt_templates/question_free_text.jinja +3 -3
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
- edsl/questions/prompt_templates/question_list.jinja +17 -17
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
- edsl/questions/prompt_templates/question_numerical.jinja +36 -36
- edsl/questions/question_registry.py +177 -177
- edsl/questions/settings.py +12 -12
- edsl/questions/templates/budget/answering_instructions.jinja +7 -7
- edsl/questions/templates/budget/question_presentation.jinja +7 -7
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
- edsl/questions/templates/extract/answering_instructions.jinja +7 -7
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
- edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
- edsl/questions/templates/list/answering_instructions.jinja +3 -3
- edsl/questions/templates/list/question_presentation.jinja +5 -5
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
- edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
- edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
- edsl/questions/templates/numerical/question_presentation.jinja +6 -6
- edsl/questions/templates/rank/answering_instructions.jinja +11 -11
- edsl/questions/templates/rank/question_presentation.jinja +15 -15
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
- edsl/questions/templates/top_k/question_presentation.jinja +22 -22
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
- edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
- edsl/results/CSSParameterizer.py +108 -108
- edsl/results/Dataset.py +424 -587
- edsl/results/DatasetExportMixin.py +731 -653
- edsl/results/DatasetTree.py +275 -295
- edsl/results/Result.py +465 -451
- edsl/results/Results.py +1165 -1172
- edsl/results/ResultsDBMixin.py +238 -0
- edsl/results/ResultsExportMixin.py +43 -45
- edsl/results/ResultsFetchMixin.py +33 -33
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/ResultsToolsMixin.py +98 -98
- edsl/results/Selector.py +135 -145
- edsl/results/TableDisplay.py +198 -125
- edsl/results/__init__.py +2 -2
- edsl/results/table_display.css +77 -77
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/FileStore.py +632 -511
- edsl/scenarios/Scenario.py +601 -498
- edsl/scenarios/ScenarioHtmlMixin.py +64 -65
- edsl/scenarios/ScenarioJoin.py +127 -131
- edsl/scenarios/ScenarioList.py +1287 -1430
- edsl/scenarios/ScenarioListExportMixin.py +52 -45
- edsl/scenarios/ScenarioListPdfMixin.py +261 -239
- edsl/scenarios/__init__.py +4 -3
- edsl/shared.py +1 -1
- edsl/study/ObjectEntry.py +173 -173
- edsl/study/ProofOfWork.py +113 -113
- edsl/study/SnapShot.py +80 -80
- edsl/study/Study.py +528 -521
- edsl/study/__init__.py +4 -4
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +326 -327
- edsl/surveys/RuleCollection.py +387 -385
- edsl/surveys/Survey.py +1801 -1229
- edsl/surveys/SurveyCSS.py +261 -273
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +179 -181
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/__init__.py +3 -5
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +56 -60
- edsl/surveys/instructions/ChangeInstruction.py +49 -48
- edsl/surveys/instructions/Instruction.py +65 -56
- edsl/surveys/instructions/InstructionCollection.py +77 -82
- edsl/templates/error_reporting/base.html +23 -23
- edsl/templates/error_reporting/exceptions_by_model.html +34 -34
- edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
- edsl/templates/error_reporting/exceptions_by_type.html +16 -16
- edsl/templates/error_reporting/interview_details.html +115 -115
- edsl/templates/error_reporting/interviews.html +19 -19
- edsl/templates/error_reporting/overview.html +4 -4
- edsl/templates/error_reporting/performance_plot.html +1 -1
- edsl/templates/error_reporting/report.css +73 -73
- edsl/templates/error_reporting/report.html +117 -117
- edsl/templates/error_reporting/report.js +25 -25
- edsl/tools/__init__.py +1 -1
- edsl/tools/clusters.py +192 -192
- edsl/tools/embeddings.py +27 -27
- edsl/tools/embeddings_plotting.py +118 -118
- edsl/tools/plotting.py +112 -112
- edsl/tools/summarize.py +18 -18
- edsl/utilities/SystemInfo.py +28 -28
- edsl/utilities/__init__.py +22 -22
- edsl/utilities/ast_utilities.py +25 -25
- edsl/utilities/data/Registry.py +6 -6
- edsl/utilities/data/__init__.py +1 -1
- edsl/utilities/data/scooter_results.json +1 -1
- edsl/utilities/decorators.py +77 -77
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
- edsl/utilities/interface.py +627 -627
- edsl/utilities/naming_utilities.py +263 -263
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +424 -436
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/LICENSE +21 -21
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/METADATA +10 -12
- edsl-0.1.39.dev3.dist-info/RECORD +277 -0
- edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
- edsl/agents/QuestionOptionProcessor.py +0 -172
- edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
- edsl/coop/CoopFunctionsMixin.py +0 -15
- edsl/coop/ExpectedParrotKeyHandler.py +0 -125
- edsl/exceptions/inference_services.py +0 -5
- edsl/inference_services/AvailableModelCacheHandler.py +0 -184
- edsl/inference_services/AvailableModelFetcher.py +0 -209
- edsl/inference_services/ServiceAvailability.py +0 -135
- edsl/inference_services/data_structures.py +0 -62
- edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -188
- edsl/jobs/FetchInvigilator.py +0 -40
- edsl/jobs/InterviewTaskManager.py +0 -98
- edsl/jobs/InterviewsConstructor.py +0 -48
- edsl/jobs/JobsComponentConstructor.py +0 -189
- edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
- edsl/jobs/RequestTokenEstimator.py +0 -30
- edsl/jobs/buckets/TokenBucketAPI.py +0 -211
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/decorators.py +0 -35
- edsl/jobs/jobs_status_enums.py +0 -9
- edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
- edsl/language_models/ComputeCost.py +0 -63
- edsl/language_models/PriceManager.py +0 -127
- edsl/language_models/RawResponseHandler.py +0 -106
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +0 -131
- edsl/notebooks/NotebookToLaTeX.py +0 -142
- edsl/questions/ExceptionExplainer.py +0 -77
- edsl/questions/HTMLQuestion.py +0 -103
- edsl/questions/LoopProcessor.py +0 -149
- edsl/questions/QuestionMatrix.py +0 -265
- edsl/questions/ResponseValidatorFactory.py +0 -28
- edsl/questions/templates/matrix/__init__.py +0 -1
- edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
- edsl/questions/templates/matrix/question_presentation.jinja +0 -20
- edsl/results/MarkdownToDocx.py +0 -122
- edsl/results/MarkdownToPDF.py +0 -111
- edsl/results/TextEditor.py +0 -50
- edsl/results/smart_objects.py +0 -96
- edsl/results/table_data_class.py +0 -12
- edsl/results/table_renderers.py +0 -118
- edsl/scenarios/ConstructDownloadLink.py +0 -109
- edsl/scenarios/DirectoryScanner.py +0 -96
- edsl/scenarios/DocumentChunker.py +0 -102
- edsl/scenarios/DocxScenario.py +0 -16
- edsl/scenarios/PdfExtractor.py +0 -40
- edsl/scenarios/ScenarioSelector.py +0 -156
- edsl/scenarios/file_methods.py +0 -85
- edsl/scenarios/handlers/__init__.py +0 -13
- edsl/scenarios/handlers/csv.py +0 -38
- edsl/scenarios/handlers/docx.py +0 -76
- edsl/scenarios/handlers/html.py +0 -37
- edsl/scenarios/handlers/json.py +0 -111
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/scenarios/handlers/md.py +0 -51
- edsl/scenarios/handlers/pdf.py +0 -68
- edsl/scenarios/handlers/png.py +0 -39
- edsl/scenarios/handlers/pptx.py +0 -105
- edsl/scenarios/handlers/py.py +0 -294
- edsl/scenarios/handlers/sql.py +0 -313
- edsl/scenarios/handlers/sqlite.py +0 -149
- edsl/scenarios/handlers/txt.py +0 -33
- edsl/surveys/ConstructDAG.py +0 -92
- edsl/surveys/EditSurvey.py +0 -221
- edsl/surveys/InstructionHandler.py +0 -100
- edsl/surveys/MemoryManagement.py +0 -72
- edsl/surveys/RuleManager.py +0 -172
- edsl/surveys/Simulator.py +0 -75
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/utilities/PrettyList.py +0 -56
- edsl/utilities/is_notebook.py +0 -18
- edsl/utilities/is_valid_variable_name.py +0 -11
- edsl/utilities/remove_edsl_version.py +0 -24
- edsl-0.1.39.dev2.dist-info/RECORD +0 -352
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/WHEEL +0 -0
edsl/results/MarkdownToPDF.py
DELETED
@@ -1,111 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
import subprocess
|
3
|
-
import os
|
4
|
-
from pathlib import Path
|
5
|
-
import tempfile
|
6
|
-
|
7
|
-
|
8
|
-
class 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
|
edsl/results/TextEditor.py
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
try:
|
2
|
-
import gradio as gr
|
3
|
-
except ImportError:
|
4
|
-
print("Gradio is not installed. Please install it using `pip install gradio`")
|
5
|
-
|
6
|
-
import time
|
7
|
-
|
8
|
-
|
9
|
-
class TextEditor:
|
10
|
-
def __init__(self, initial_text=""):
|
11
|
-
self.text = initial_text
|
12
|
-
self._text_saved = False
|
13
|
-
|
14
|
-
def save_text(self, new_text):
|
15
|
-
self.text = new_text
|
16
|
-
self._text_saved = True
|
17
|
-
return "Text saved successfully!"
|
18
|
-
|
19
|
-
def edit_gui(self):
|
20
|
-
js_code = """
|
21
|
-
async (text) => {
|
22
|
-
await navigator.clipboard.writeText(text);
|
23
|
-
return "Copied to clipboard!";
|
24
|
-
}
|
25
|
-
"""
|
26
|
-
|
27
|
-
with gr.Blocks() as interface:
|
28
|
-
text_area = gr.Textbox(
|
29
|
-
value=self.text, lines=10, label="Edit Text", placeholder="Type here..."
|
30
|
-
)
|
31
|
-
|
32
|
-
with gr.Row():
|
33
|
-
save_btn = gr.Button("Save")
|
34
|
-
copy_btn = gr.Button("Copy to Clipboard")
|
35
|
-
|
36
|
-
output = gr.Textbox(label="Status")
|
37
|
-
|
38
|
-
save_btn.click(fn=self.save_text, inputs=[text_area], outputs=[output])
|
39
|
-
|
40
|
-
# Add copy functionality
|
41
|
-
copy_btn.click(
|
42
|
-
fn=None, inputs=text_area, outputs=output, api_name=False, js=js_code
|
43
|
-
)
|
44
|
-
|
45
|
-
interface.launch(share=False, prevent_thread_lock=True)
|
46
|
-
|
47
|
-
while not self._text_saved:
|
48
|
-
time.sleep(0.1)
|
49
|
-
|
50
|
-
return self.text
|
edsl/results/smart_objects.py
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
|
-
|
4
|
-
class SmartInt(int):
|
5
|
-
pass
|
6
|
-
|
7
|
-
|
8
|
-
class SmartFloat(float):
|
9
|
-
pass
|
10
|
-
|
11
|
-
|
12
|
-
class SmartStr(str):
|
13
|
-
def clipboard(self) -> None:
|
14
|
-
try:
|
15
|
-
import pyperclip
|
16
|
-
except ImportError:
|
17
|
-
print(
|
18
|
-
"pyperclip is not installed. Run `pip install pyperclip` to install it."
|
19
|
-
)
|
20
|
-
return None
|
21
|
-
|
22
|
-
pyperclip.copy(self)
|
23
|
-
print("Text copied to clipboard.")
|
24
|
-
|
25
|
-
def write(self, filename: str):
|
26
|
-
with open(filename, "w") as f:
|
27
|
-
f.write(str(self))
|
28
|
-
return None
|
29
|
-
|
30
|
-
def _repr_html_(self):
|
31
|
-
pass
|
32
|
-
|
33
|
-
def markdown(self):
|
34
|
-
return SmartMarkdown(self)
|
35
|
-
|
36
|
-
def pdf(self, filename: Optional[str] = None): # Markdown will have this as well
|
37
|
-
# renders the markdown as a pdf that can be downloaded
|
38
|
-
from edsl.results.MarkdownToPDF import MarkdownToPDF
|
39
|
-
|
40
|
-
return MarkdownToPDF(self, filename).preview()
|
41
|
-
|
42
|
-
def docx(self, filename: Optional[str] = None):
|
43
|
-
# renders the markdown as a docx that can be downloaded
|
44
|
-
from edsl.results.MarkdownToDocx import MarkdownToDocx
|
45
|
-
|
46
|
-
return MarkdownToDocx(self, filename).preview()
|
47
|
-
|
48
|
-
def edit(self):
|
49
|
-
from edsl.results.TextEditor import TextEditor
|
50
|
-
|
51
|
-
editor = TextEditor(self)
|
52
|
-
self = self.__class__(editor.edit_gui())
|
53
|
-
# print(f"Updated text: {self}")
|
54
|
-
|
55
|
-
|
56
|
-
class SmartMarkdown(SmartStr):
|
57
|
-
def _repr_markdown_(self):
|
58
|
-
return self
|
59
|
-
|
60
|
-
def _repr_html_(self):
|
61
|
-
from IPython.display import Markdown, display
|
62
|
-
|
63
|
-
display(Markdown(self))
|
64
|
-
|
65
|
-
|
66
|
-
class SmartLaTeX(SmartStr):
|
67
|
-
def _repr_html_(self):
|
68
|
-
print(self)
|
69
|
-
|
70
|
-
def pdf(self, filename: Optional[str] = None):
|
71
|
-
from edsl.results.LaTeXToPDF import LaTeXToPDF
|
72
|
-
|
73
|
-
return LaTeXToPDF(self, filename).preview()
|
74
|
-
|
75
|
-
def docx(self, filename: Optional[str] = None):
|
76
|
-
from edsl.results.LaTeXToDocx import LaTeXToDocx
|
77
|
-
|
78
|
-
return LaTeXToDocx(self, filename).preview()
|
79
|
-
|
80
|
-
def edit(self):
|
81
|
-
from edsl.results.TextEditor import TextEditor
|
82
|
-
|
83
|
-
editor = TextEditor(self)
|
84
|
-
self = self.__class__(editor.edit_gui())
|
85
|
-
# print(f"Updated LaTeX: {self}")
|
86
|
-
|
87
|
-
|
88
|
-
class FirstObject:
|
89
|
-
def __new__(self, value):
|
90
|
-
if isinstance(value, int):
|
91
|
-
return SmartInt(value)
|
92
|
-
if isinstance(value, float):
|
93
|
-
return SmartFloat(value)
|
94
|
-
if isinstance(value, str):
|
95
|
-
return SmartStr(value)
|
96
|
-
return value
|
edsl/results/table_data_class.py
DELETED
edsl/results/table_renderers.py
DELETED
@@ -1,118 +0,0 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
from edsl.results.table_data_class import TableData
|
3
|
-
|
4
|
-
|
5
|
-
class DataTablesRendererABC(ABC):
|
6
|
-
def __init__(self, table_data: TableData):
|
7
|
-
self.table_data = table_data
|
8
|
-
|
9
|
-
@abstractmethod
|
10
|
-
def render_html(self) -> str:
|
11
|
-
pass
|
12
|
-
|
13
|
-
|
14
|
-
class DataTablesRenderer(DataTablesRendererABC):
|
15
|
-
"""Interactive DataTables renderer implementation"""
|
16
|
-
|
17
|
-
def render_html(self) -> str:
|
18
|
-
html_template = """
|
19
|
-
<!DOCTYPE html>
|
20
|
-
<html>
|
21
|
-
<head>
|
22
|
-
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
|
23
|
-
<link href="https://cdnjs.cloudflare.com/ajax/libs/datatables.net-bs5/1.13.6/dataTables.bootstrap5.min.css" rel="stylesheet">
|
24
|
-
<link href="https://cdnjs.cloudflare.com/ajax/libs/datatables.net-buttons-bs5/2.4.1/buttons.bootstrap5.min.css" rel="stylesheet">
|
25
|
-
<link href="https://cdnjs.cloudflare.com/ajax/libs/datatables.net-responsive-bs5/2.4.1/responsive.bootstrap5.min.css" rel="stylesheet">
|
26
|
-
<style>
|
27
|
-
{css}
|
28
|
-
</style>
|
29
|
-
</head>
|
30
|
-
<body>
|
31
|
-
<div class="container">
|
32
|
-
<table id="interactive-table" class="table table-striped" style="width:100%">
|
33
|
-
<thead>
|
34
|
-
<tr>{header_cells}</tr>
|
35
|
-
</thead>
|
36
|
-
<tbody>{body_rows}</tbody>
|
37
|
-
</table>
|
38
|
-
</div>
|
39
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
|
40
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/datatables.net/1.13.6/jquery.dataTables.min.js"></script>
|
41
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/datatables.net-bs5/1.13.6/dataTables.bootstrap5.min.js"></script>
|
42
|
-
<script>
|
43
|
-
$(document).ready(function() {{
|
44
|
-
$('#interactive-table').DataTable({{
|
45
|
-
pageLength: 10,
|
46
|
-
lengthMenu: [[5, 10, 25, -1], [5, 10, 25, "All"]],
|
47
|
-
scrollX: true,
|
48
|
-
responsive: true,
|
49
|
-
dom: 'Bfrtip',
|
50
|
-
buttons: [
|
51
|
-
{{
|
52
|
-
extend: 'colvis',
|
53
|
-
text: 'Show/Hide Columns'
|
54
|
-
}}
|
55
|
-
]
|
56
|
-
}});
|
57
|
-
}});
|
58
|
-
</script>
|
59
|
-
</body>
|
60
|
-
</html>
|
61
|
-
"""
|
62
|
-
|
63
|
-
header_cells = "".join(
|
64
|
-
f"<th>{header}</th>" for header in self.table_data.headers
|
65
|
-
)
|
66
|
-
body_rows = ""
|
67
|
-
for row in self.table_data.data:
|
68
|
-
body_rows += "<tr>"
|
69
|
-
body_rows += "".join(f"<td>{cell}</td>" for cell in row)
|
70
|
-
body_rows += "</tr>"
|
71
|
-
|
72
|
-
parameters = self.table_data.parameters or {}
|
73
|
-
css = self.get_css()
|
74
|
-
if hasattr(self, "css_parameterizer"):
|
75
|
-
css = self.css_parameterizer(css).apply_parameters(parameters)
|
76
|
-
|
77
|
-
return html_template.format(
|
78
|
-
css=css, header_cells=header_cells, body_rows=body_rows
|
79
|
-
)
|
80
|
-
|
81
|
-
@classmethod
|
82
|
-
def get_css(cls) -> str:
|
83
|
-
"""Load CSS content from the file next to this module"""
|
84
|
-
css_path = Path(__file__).parent / "table_display.css"
|
85
|
-
return css_path.read_text()
|
86
|
-
|
87
|
-
|
88
|
-
class PandasStyleRenderer(DataTablesRendererABC):
|
89
|
-
"""Pandas-based styled renderer implementation"""
|
90
|
-
|
91
|
-
def render_html(self) -> str:
|
92
|
-
import pandas as pd
|
93
|
-
|
94
|
-
from contextlib import redirect_stderr
|
95
|
-
import io
|
96
|
-
|
97
|
-
stderr = io.StringIO()
|
98
|
-
with redirect_stderr(stderr):
|
99
|
-
if self.table_data.raw_data_set is not None and hasattr(
|
100
|
-
self.table_data.raw_data_set, "to_pandas"
|
101
|
-
):
|
102
|
-
df = self.table_data.raw_data_set.to_pandas()
|
103
|
-
else:
|
104
|
-
df = pd.DataFrame(self.table_data.data, columns=self.table_data.headers)
|
105
|
-
|
106
|
-
styled_df = df.style.set_properties(
|
107
|
-
**{"text-align": "left"}
|
108
|
-
).background_gradient()
|
109
|
-
|
110
|
-
return f"""
|
111
|
-
<div style="max-height: 500px; overflow-y: auto;">
|
112
|
-
{styled_df.to_html()}
|
113
|
-
</div>
|
114
|
-
"""
|
115
|
-
|
116
|
-
@classmethod
|
117
|
-
def get_css(cls) -> str:
|
118
|
-
return "" # Pandas styling handles its own CSS
|
@@ -1,109 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import mimetypes
|
3
|
-
|
4
|
-
|
5
|
-
class ConstructDownloadLink:
|
6
|
-
"""
|
7
|
-
A class to create HTML download links for FileStore objects.
|
8
|
-
The links can be displayed in Jupyter notebooks or other web interfaces.
|
9
|
-
"""
|
10
|
-
|
11
|
-
def __init__(self, filestore):
|
12
|
-
"""
|
13
|
-
Initialize with a FileStore object.
|
14
|
-
|
15
|
-
Args:
|
16
|
-
filestore: A FileStore object containing the file to be made downloadable
|
17
|
-
"""
|
18
|
-
self.filestore = filestore
|
19
|
-
|
20
|
-
def create_link(self, custom_filename=None, style=None):
|
21
|
-
from IPython.display import HTML
|
22
|
-
|
23
|
-
html = self.html_create_link(custom_filename, style)
|
24
|
-
return HTML(html)
|
25
|
-
|
26
|
-
def html_create_link(self, custom_filename=None, style=None):
|
27
|
-
"""
|
28
|
-
Create an HTML download link for the file.
|
29
|
-
|
30
|
-
Args:
|
31
|
-
custom_filename (str, optional): Custom name for the downloaded file.
|
32
|
-
If None, uses original filename.
|
33
|
-
style (dict, optional): Custom CSS styles for the download button.
|
34
|
-
If None, uses default styling.
|
35
|
-
|
36
|
-
Returns:
|
37
|
-
IPython.display.HTML: HTML object containing the download link
|
38
|
-
"""
|
39
|
-
|
40
|
-
# Get filename from path or use custom filename
|
41
|
-
original_filename = os.path.basename(self.filestore.path)
|
42
|
-
filename = custom_filename or original_filename
|
43
|
-
|
44
|
-
# Use the base64 string already stored in FileStore
|
45
|
-
b64_data = self.filestore.base64_string
|
46
|
-
|
47
|
-
# Use mime type from FileStore or guess it
|
48
|
-
mime_type = self.filestore.mime_type
|
49
|
-
|
50
|
-
# Default style if none provided
|
51
|
-
default_style = {
|
52
|
-
"background-color": "#4CAF50",
|
53
|
-
"color": "white",
|
54
|
-
"padding": "10px 20px",
|
55
|
-
"text-decoration": "none",
|
56
|
-
"border-radius": "4px",
|
57
|
-
"display": "inline-block",
|
58
|
-
"margin": "10px 0",
|
59
|
-
"font-family": "sans-serif",
|
60
|
-
"cursor": "pointer",
|
61
|
-
}
|
62
|
-
|
63
|
-
button_style = style or default_style
|
64
|
-
style_str = "; ".join(f"{k}: {v}" for k, v in button_style.items())
|
65
|
-
|
66
|
-
html = f"""
|
67
|
-
<a download="{filename}"
|
68
|
-
href="data:{mime_type};base64,{b64_data}"
|
69
|
-
style="{style_str}">
|
70
|
-
Download {filename}
|
71
|
-
</a>
|
72
|
-
"""
|
73
|
-
return html
|
74
|
-
|
75
|
-
def create_multiple_links(self, files, custom_filenames=None, style=None):
|
76
|
-
"""
|
77
|
-
Create multiple download links at once.
|
78
|
-
Useful when you want to provide different versions of the same file
|
79
|
-
or related files together.
|
80
|
-
|
81
|
-
Args:
|
82
|
-
files (list): List of FileStore objects
|
83
|
-
custom_filenames (list, optional): List of custom filenames for downloads
|
84
|
-
style (dict, optional): Custom CSS styles for the download buttons
|
85
|
-
|
86
|
-
Returns:
|
87
|
-
IPython.display.HTML: HTML object containing all download links
|
88
|
-
"""
|
89
|
-
if custom_filenames is None:
|
90
|
-
custom_filenames = [None] * len(files)
|
91
|
-
|
92
|
-
html_parts = []
|
93
|
-
for file_obj, custom_name in zip(files, custom_filenames):
|
94
|
-
link_creator = ConstructDownloadLink(file_obj)
|
95
|
-
html_parts.append(
|
96
|
-
link_creator.create_link(
|
97
|
-
custom_filename=custom_name, style=style
|
98
|
-
)._repr_html_()
|
99
|
-
)
|
100
|
-
|
101
|
-
return HTML(
|
102
|
-
'<div style="display: flex; gap: 10px;">' + "".join(html_parts) + "</div>"
|
103
|
-
)
|
104
|
-
|
105
|
-
|
106
|
-
if __name__ == "__main__":
|
107
|
-
import doctest
|
108
|
-
|
109
|
-
doctest.testmod()
|
@@ -1,96 +0,0 @@
|
|
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)
|
@@ -1,102 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
from typing import Optional, Generator, TYPE_CHECKING
|
3
|
-
import copy
|
4
|
-
|
5
|
-
if TYPE_CHECKING:
|
6
|
-
from edsl.scenarios.Scenario import Scenario
|
7
|
-
from edsl.scenarios.ScenarioList import ScenarioList
|
8
|
-
|
9
|
-
|
10
|
-
class DocumentChunker:
|
11
|
-
def __init__(self, scenario: "Scenario"):
|
12
|
-
self.scenario = scenario
|
13
|
-
|
14
|
-
@staticmethod
|
15
|
-
def _line_chunks(text, num_lines: int) -> Generator[str, None, None]:
|
16
|
-
"""Split a text into chunks of a given size.
|
17
|
-
|
18
|
-
:param text: The text to split.
|
19
|
-
:param num_lines: The number of lines in each chunk.
|
20
|
-
|
21
|
-
Example:
|
22
|
-
|
23
|
-
>>> list(DocumentChunker._line_chunks("This is a test.\\nThis is a test. This is a test.", 1))
|
24
|
-
['This is a test.', 'This is a test. This is a test.']
|
25
|
-
"""
|
26
|
-
lines = text.split("\n")
|
27
|
-
for i in range(0, len(lines), num_lines):
|
28
|
-
chunk = "\n".join(lines[i : i + num_lines])
|
29
|
-
yield chunk
|
30
|
-
|
31
|
-
@staticmethod
|
32
|
-
def _word_chunks(text, num_words: int) -> Generator[str, None, None]:
|
33
|
-
"""Split a text into chunks of a given size.
|
34
|
-
|
35
|
-
:param text: The text to split.
|
36
|
-
:param num_words: The number of words in each chunk.
|
37
|
-
|
38
|
-
Example:
|
39
|
-
|
40
|
-
>>> list(DocumentChunker._word_chunks("This is a test.", 2))
|
41
|
-
['This is', 'a test.']
|
42
|
-
"""
|
43
|
-
words = text.split()
|
44
|
-
for i in range(0, len(words), num_words):
|
45
|
-
chunk = " ".join(words[i : i + num_words])
|
46
|
-
yield chunk
|
47
|
-
|
48
|
-
def chunk(
|
49
|
-
self,
|
50
|
-
field,
|
51
|
-
num_words: Optional[int] = None,
|
52
|
-
num_lines: Optional[int] = None,
|
53
|
-
include_original=False,
|
54
|
-
hash_original=False,
|
55
|
-
) -> ScenarioList:
|
56
|
-
"""Split a field into chunks of a given size.
|
57
|
-
|
58
|
-
:param field: The field to split.
|
59
|
-
:param num_words: The number of words in each chunk.
|
60
|
-
:param num_lines: The number of lines in each chunk.
|
61
|
-
:param include_original: Whether to include the original field in the new scenarios.
|
62
|
-
:param hash_original: Whether to hash the original field in the new scenarios.
|
63
|
-
|
64
|
-
If you specify `include_original=True`, the original field will be included in the new scenarios with an "_original" suffix.
|
65
|
-
"""
|
66
|
-
from edsl.scenarios.ScenarioList import ScenarioList
|
67
|
-
import hashlib
|
68
|
-
|
69
|
-
if num_words is not None:
|
70
|
-
chunks = list(self._word_chunks(self.scenario[field], num_words))
|
71
|
-
|
72
|
-
if num_lines is not None:
|
73
|
-
chunks = list(self._line_chunks(self.scenario[field], num_lines))
|
74
|
-
|
75
|
-
if num_words is None and num_lines is None:
|
76
|
-
raise ValueError("You must specify either num_words or num_lines.")
|
77
|
-
|
78
|
-
if num_words is not None and num_lines is not None:
|
79
|
-
raise ValueError(
|
80
|
-
"You must specify either num_words or num_lines, but not both."
|
81
|
-
)
|
82
|
-
|
83
|
-
scenarios = []
|
84
|
-
for i, chunk in enumerate(chunks):
|
85
|
-
new_scenario = copy.deepcopy(self.scenario)
|
86
|
-
new_scenario[field] = chunk
|
87
|
-
new_scenario[field + "_chunk"] = i
|
88
|
-
if include_original:
|
89
|
-
if hash_original:
|
90
|
-
new_scenario[field + "_original"] = hashlib.md5(
|
91
|
-
self.scenario[field].encode()
|
92
|
-
).hexdigest()
|
93
|
-
else:
|
94
|
-
new_scenario[field + "_original"] = self.scenario[field]
|
95
|
-
scenarios.append(new_scenario)
|
96
|
-
return ScenarioList(scenarios)
|
97
|
-
|
98
|
-
|
99
|
-
if __name__ == "__main__":
|
100
|
-
import doctest
|
101
|
-
|
102
|
-
doctest.testmod()
|