edsl 0.1.39.dev3__py3-none-any.whl → 0.1.39.dev4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edsl/Base.py +413 -332
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +57 -49
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +1071 -867
- edsl/agents/AgentList.py +551 -413
- edsl/agents/Invigilator.py +284 -233
- edsl/agents/InvigilatorBase.py +257 -270
- edsl/agents/PromptConstructor.py +272 -354
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/__init__.py +2 -3
- edsl/agents/descriptors.py +99 -99
- edsl/agents/prompt_helpers.py +129 -129
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +130 -117
- edsl/auto/StageBase.py +243 -230
- edsl/auto/StageGenerateSurvey.py +178 -178
- edsl/auto/StageLabelQuestions.py +125 -125
- edsl/auto/StagePersona.py +61 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
- edsl/auto/StagePersonaDimensionValues.py +74 -74
- edsl/auto/StagePersonaDimensions.py +69 -69
- edsl/auto/StageQuestions.py +74 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +218 -224
- edsl/base/Base.py +279 -279
- edsl/config.py +177 -157
- edsl/conversation/Conversation.py +290 -290
- edsl/conversation/car_buying.py +59 -58
- edsl/conversation/chips.py +95 -95
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +1106 -1028
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +573 -555
- edsl/data/CacheEntry.py +230 -233
- edsl/data/CacheHandler.py +168 -149
- edsl/data/RemoteCacheSync.py +186 -78
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +5 -4
- edsl/data/hack.py +10 -0
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +74 -73
- edsl/enums.py +202 -175
- edsl/exceptions/BaseException.py +21 -21
- edsl/exceptions/__init__.py +54 -54
- edsl/exceptions/agents.py +54 -42
- edsl/exceptions/cache.py +5 -5
- edsl/exceptions/configuration.py +16 -16
- edsl/exceptions/coop.py +10 -10
- edsl/exceptions/data.py +14 -14
- edsl/exceptions/general.py +34 -34
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +109 -91
- edsl/exceptions/results.py +29 -29
- edsl/exceptions/scenarios.py +29 -22
- edsl/exceptions/surveys.py +37 -37
- edsl/inference_services/AnthropicService.py +106 -87
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +118 -120
- edsl/inference_services/AzureAI.py +215 -217
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +143 -148
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +80 -147
- edsl/inference_services/InferenceServicesCollection.py +138 -97
- edsl/inference_services/MistralAIService.py +120 -123
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +236 -224
- edsl/inference_services/PerplexityService.py +160 -163
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +90 -89
- edsl/inference_services/TogetherAIService.py +172 -170
- edsl/inference_services/data_structures.py +134 -0
- edsl/inference_services/models_available_cache.py +118 -118
- edsl/inference_services/rate_limits_cache.py +25 -25
- edsl/inference_services/registry.py +41 -41
- edsl/inference_services/write_available.py +10 -10
- edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +43 -56
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +823 -898
- edsl/jobs/JobsChecks.py +172 -147
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +270 -268
- edsl/jobs/JobsRemoteInferenceHandler.py +311 -239
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/async_interview_runner.py +138 -0
- edsl/jobs/buckets/BucketCollection.py +104 -63
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +283 -251
- edsl/jobs/buckets/TokenBucketAPI.py +211 -0
- edsl/jobs/buckets/TokenBucketClient.py +191 -0
- edsl/jobs/check_survey_scenario_compatibility.py +85 -0
- edsl/jobs/data_structures.py +120 -0
- edsl/jobs/decorators.py +35 -0
- edsl/jobs/interviews/Interview.py +396 -661
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
- edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
- edsl/jobs/interviews/InterviewStatistic.py +63 -63
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
- edsl/jobs/interviews/InterviewStatusLog.py +92 -92
- edsl/jobs/interviews/ReportErrors.py +66 -66
- edsl/jobs/interviews/interview_status_enum.py +9 -9
- edsl/jobs/jobs_status_enums.py +9 -0
- edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
- edsl/jobs/results_exceptions_handler.py +98 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +151 -466
- edsl/jobs/runners/JobsRunnerStatus.py +297 -330
- edsl/jobs/tasks/QuestionTaskCreator.py +244 -242
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +470 -450
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +161 -163
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +626 -668
- edsl/language_models/ModelList.py +164 -155
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/__init__.py +2 -3
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- edsl/language_models/key_management/KeyLookup.py +63 -0
- edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
- edsl/language_models/key_management/KeyLookupCollection.py +38 -0
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +131 -0
- edsl/language_models/model.py +256 -0
- edsl/language_models/repair.py +156 -156
- edsl/language_models/utilities.py +65 -64
- edsl/notebooks/Notebook.py +263 -258
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +352 -362
- edsl/prompts/__init__.py +2 -2
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +518 -664
- edsl/questions/QuestionBasePromptsMixin.py +221 -217
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +180 -182
- edsl/questions/QuestionFreeText.py +113 -114
- edsl/questions/QuestionFunctional.py +166 -166
- edsl/questions/QuestionList.py +223 -231
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +330 -286
- edsl/questions/QuestionNumerical.py +151 -153
- edsl/questions/QuestionRank.py +314 -324
- edsl/questions/Quick.py +41 -41
- edsl/questions/SimpleAskMixin.py +74 -73
- edsl/questions/__init__.py +27 -26
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +334 -289
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/data_structures.py +20 -0
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +90 -87
- edsl/questions/derived/QuestionTopK.py +93 -93
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +427 -413
- edsl/questions/loop_processor.py +149 -0
- edsl/questions/prompt_templates/question_budget.jinja +13 -13
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
- edsl/questions/prompt_templates/question_extract.jinja +11 -11
- edsl/questions/prompt_templates/question_free_text.jinja +3 -3
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
- edsl/questions/prompt_templates/question_list.jinja +17 -17
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
- edsl/questions/prompt_templates/question_numerical.jinja +36 -36
- edsl/questions/{QuestionBaseGenMixin.py → question_base_gen_mixin.py} +168 -161
- edsl/questions/question_registry.py +177 -177
- edsl/questions/{RegisterQuestionsMeta.py → register_questions_meta.py} +71 -71
- edsl/questions/{ResponseValidatorABC.py → response_validator_abc.py} +188 -174
- edsl/questions/response_validator_factory.py +34 -0
- edsl/questions/settings.py +12 -12
- edsl/questions/templates/budget/answering_instructions.jinja +7 -7
- edsl/questions/templates/budget/question_presentation.jinja +7 -7
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
- edsl/questions/templates/extract/answering_instructions.jinja +7 -7
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
- edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
- edsl/questions/templates/list/answering_instructions.jinja +3 -3
- edsl/questions/templates/list/question_presentation.jinja +5 -5
- edsl/questions/templates/matrix/__init__.py +1 -0
- edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
- edsl/questions/templates/matrix/question_presentation.jinja +20 -0
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
- edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
- edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
- edsl/questions/templates/numerical/question_presentation.jinja +6 -6
- edsl/questions/templates/rank/answering_instructions.jinja +11 -11
- edsl/questions/templates/rank/question_presentation.jinja +15 -15
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
- edsl/questions/templates/top_k/question_presentation.jinja +22 -22
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
- edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
- edsl/results/CSSParameterizer.py +108 -108
- edsl/results/Dataset.py +587 -424
- edsl/results/DatasetExportMixin.py +594 -731
- edsl/results/DatasetTree.py +295 -275
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +557 -465
- edsl/results/Results.py +1183 -1165
- edsl/results/ResultsExportMixin.py +45 -43
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/TableDisplay.py +125 -198
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +2 -2
- edsl/results/file_exports.py +252 -0
- edsl/results/{ResultsFetchMixin.py → results_fetch_mixin.py} +33 -33
- edsl/results/{Selector.py → results_selector.py} +145 -135
- edsl/results/{ResultsToolsMixin.py → results_tools_mixin.py} +98 -98
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_display.css +77 -77
- edsl/results/table_renderers.py +118 -0
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +511 -632
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +498 -601
- edsl/scenarios/ScenarioHtmlMixin.py +65 -64
- edsl/scenarios/ScenarioList.py +1458 -1287
- edsl/scenarios/ScenarioListExportMixin.py +45 -52
- edsl/scenarios/ScenarioListPdfMixin.py +239 -261
- edsl/scenarios/__init__.py +3 -4
- edsl/scenarios/directory_scanner.py +96 -0
- edsl/scenarios/file_methods.py +85 -0
- edsl/scenarios/handlers/__init__.py +13 -0
- edsl/scenarios/handlers/csv.py +38 -0
- edsl/scenarios/handlers/docx.py +76 -0
- edsl/scenarios/handlers/html.py +37 -0
- edsl/scenarios/handlers/json.py +111 -0
- edsl/scenarios/handlers/latex.py +5 -0
- edsl/scenarios/handlers/md.py +51 -0
- edsl/scenarios/handlers/pdf.py +68 -0
- edsl/scenarios/handlers/png.py +39 -0
- edsl/scenarios/handlers/pptx.py +105 -0
- edsl/scenarios/handlers/py.py +294 -0
- edsl/scenarios/handlers/sql.py +313 -0
- edsl/scenarios/handlers/sqlite.py +149 -0
- edsl/scenarios/handlers/txt.py +33 -0
- edsl/scenarios/{ScenarioJoin.py → scenario_join.py} +131 -127
- edsl/scenarios/scenario_selector.py +156 -0
- edsl/shared.py +1 -1
- edsl/study/ObjectEntry.py +173 -173
- edsl/study/ProofOfWork.py +113 -113
- edsl/study/SnapShot.py +80 -80
- edsl/study/Study.py +521 -528
- edsl/study/__init__.py +4 -4
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +327 -326
- edsl/surveys/RuleCollection.py +385 -387
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +1280 -1801
- edsl/surveys/SurveyCSS.py +273 -261
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +181 -179
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +5 -3
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +60 -56
- edsl/surveys/instructions/ChangeInstruction.py +48 -49
- edsl/surveys/instructions/Instruction.py +56 -65
- edsl/surveys/instructions/InstructionCollection.py +82 -77
- edsl/templates/error_reporting/base.html +23 -23
- edsl/templates/error_reporting/exceptions_by_model.html +34 -34
- edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
- edsl/templates/error_reporting/exceptions_by_type.html +16 -16
- edsl/templates/error_reporting/interview_details.html +115 -115
- edsl/templates/error_reporting/interviews.html +19 -19
- edsl/templates/error_reporting/overview.html +4 -4
- edsl/templates/error_reporting/performance_plot.html +1 -1
- edsl/templates/error_reporting/report.css +73 -73
- edsl/templates/error_reporting/report.html +117 -117
- edsl/templates/error_reporting/report.js +25 -25
- edsl/test_h +1 -0
- edsl/tools/__init__.py +1 -1
- edsl/tools/clusters.py +192 -192
- edsl/tools/embeddings.py +27 -27
- edsl/tools/embeddings_plotting.py +118 -118
- edsl/tools/plotting.py +112 -112
- edsl/tools/summarize.py +18 -18
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/SystemInfo.py +28 -28
- edsl/utilities/__init__.py +22 -22
- edsl/utilities/ast_utilities.py +25 -25
- edsl/utilities/data/Registry.py +6 -6
- edsl/utilities/data/__init__.py +1 -1
- edsl/utilities/data/scooter_results.json +1 -1
- edsl/utilities/decorators.py +77 -77
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
- edsl/utilities/gcp_bucket/example.py +50 -0
- edsl/utilities/interface.py +627 -627
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/naming_utilities.py +263 -263
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +436 -424
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev4.dist-info}/LICENSE +21 -21
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev4.dist-info}/METADATA +13 -11
- edsl-0.1.39.dev4.dist-info/RECORD +361 -0
- edsl/language_models/KeyLookup.py +0 -30
- edsl/language_models/registry.py +0 -190
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/results/ResultsDBMixin.py +0 -238
- edsl-0.1.39.dev3.dist-info/RECORD +0 -277
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev4.dist-info}/WHEEL +0 -0
edsl/results/tree_explore.py
CHANGED
@@ -1,115 +1,115 @@
|
|
1
|
-
from collections import defaultdict
|
2
|
-
from typing import List, Dict, Any
|
3
|
-
import json
|
4
|
-
|
5
|
-
|
6
|
-
class FoldableHTMLTableGenerator:
|
7
|
-
def __init__(self, data: List[Dict[str, Any]]):
|
8
|
-
self.data = data
|
9
|
-
|
10
|
-
def tree(self, fold_attributes: List[str], drop: List[str] = None) -> Dict:
|
11
|
-
def nested_dict():
|
12
|
-
return defaultdict(nested_dict)
|
13
|
-
|
14
|
-
result = nested_dict()
|
15
|
-
drop = drop or [] # Use an empty list if drop is None
|
16
|
-
|
17
|
-
for item in self.data:
|
18
|
-
current = result
|
19
|
-
for attr in fold_attributes:
|
20
|
-
current = current[item[attr]]
|
21
|
-
|
22
|
-
row = {
|
23
|
-
k: v
|
24
|
-
for k, v in item.items()
|
25
|
-
if k not in fold_attributes and k not in drop
|
26
|
-
}
|
27
|
-
if "_rows" not in current:
|
28
|
-
current["_rows"] = []
|
29
|
-
current["_rows"].append(row)
|
30
|
-
|
31
|
-
return result
|
32
|
-
|
33
|
-
def generate_html(self, tree, fold_attributes: List[str]) -> str:
|
34
|
-
html_content = """
|
35
|
-
<!DOCTYPE html>
|
36
|
-
<html lang="en">
|
37
|
-
<head>
|
38
|
-
<meta charset="UTF-8">
|
39
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
40
|
-
<title>Foldable Nested Table</title>
|
41
|
-
<style>
|
42
|
-
.folding-section { margin-left: 20px; }
|
43
|
-
.fold-button { cursor: pointer; margin: 5px 0; }
|
44
|
-
table { border-collapse: collapse; margin-top: 10px; }
|
45
|
-
th, td { border: 1px solid black; padding: 5px; }
|
46
|
-
.attribute-label { font-weight: bold; }
|
47
|
-
</style>
|
48
|
-
</head>
|
49
|
-
<body>
|
50
|
-
<div id="root"></div>
|
51
|
-
<script>
|
52
|
-
function toggleFold(id) {
|
53
|
-
const element = document.getElementById(id);
|
54
|
-
element.style.display = element.style.display === 'none' ? 'block' : 'none';
|
55
|
-
}
|
56
|
-
|
57
|
-
function createFoldableSection(data, path = [], attributes = %s) {
|
58
|
-
const container = document.createElement('div');
|
59
|
-
container.className = 'folding-section';
|
60
|
-
|
61
|
-
for (const [key, value] of Object.entries(data)) {
|
62
|
-
if (key === '_rows') {
|
63
|
-
const table = document.createElement('table');
|
64
|
-
const headerRow = table.insertRow();
|
65
|
-
const headers = Object.keys(value[0]);
|
66
|
-
headers.forEach(header => {
|
67
|
-
const th = document.createElement('th');
|
68
|
-
th.textContent = header;
|
69
|
-
headerRow.appendChild(th);
|
70
|
-
});
|
71
|
-
value.forEach(row => {
|
72
|
-
const tableRow = table.insertRow();
|
73
|
-
headers.forEach(header => {
|
74
|
-
const cell = tableRow.insertCell();
|
75
|
-
cell.textContent = row[header];
|
76
|
-
});
|
77
|
-
});
|
78
|
-
container.appendChild(table);
|
79
|
-
} else {
|
80
|
-
const button = document.createElement('button');
|
81
|
-
const attributeType = attributes[path.length];
|
82
|
-
button.innerHTML = `<span class="attribute-label">${attributeType}:</span> ${key}`;
|
83
|
-
button.className = 'fold-button';
|
84
|
-
const sectionId = `section-${path.join('-')}-${key}`;
|
85
|
-
button.onclick = () => toggleFold(sectionId);
|
86
|
-
container.appendChild(button);
|
87
|
-
|
88
|
-
const section = document.createElement('div');
|
89
|
-
section.id = sectionId;
|
90
|
-
section.style.display = 'none';
|
91
|
-
section.appendChild(createFoldableSection(value, [...path, key], attributes));
|
92
|
-
container.appendChild(section);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
return container;
|
97
|
-
}
|
98
|
-
|
99
|
-
const treeData = %s;
|
100
|
-
document.getElementById('root').appendChild(createFoldableSection(treeData));
|
101
|
-
</script>
|
102
|
-
</body>
|
103
|
-
</html>
|
104
|
-
"""
|
105
|
-
|
106
|
-
return html_content % (json.dumps(fold_attributes), json.dumps(tree))
|
107
|
-
|
108
|
-
def save_html(self, fold_attributes: List[str], filename: str = "output.html"):
|
109
|
-
tree = self.tree(fold_attributes)
|
110
|
-
html_content = self.generate_html(tree, fold_attributes)
|
111
|
-
|
112
|
-
with open(filename, "w", encoding="utf-8") as f:
|
113
|
-
f.write(html_content)
|
114
|
-
|
115
|
-
print(f"HTML file has been generated: {filename}")
|
1
|
+
from collections import defaultdict
|
2
|
+
from typing import List, Dict, Any
|
3
|
+
import json
|
4
|
+
|
5
|
+
|
6
|
+
class FoldableHTMLTableGenerator:
|
7
|
+
def __init__(self, data: List[Dict[str, Any]]):
|
8
|
+
self.data = data
|
9
|
+
|
10
|
+
def tree(self, fold_attributes: List[str], drop: List[str] = None) -> Dict:
|
11
|
+
def nested_dict():
|
12
|
+
return defaultdict(nested_dict)
|
13
|
+
|
14
|
+
result = nested_dict()
|
15
|
+
drop = drop or [] # Use an empty list if drop is None
|
16
|
+
|
17
|
+
for item in self.data:
|
18
|
+
current = result
|
19
|
+
for attr in fold_attributes:
|
20
|
+
current = current[item[attr]]
|
21
|
+
|
22
|
+
row = {
|
23
|
+
k: v
|
24
|
+
for k, v in item.items()
|
25
|
+
if k not in fold_attributes and k not in drop
|
26
|
+
}
|
27
|
+
if "_rows" not in current:
|
28
|
+
current["_rows"] = []
|
29
|
+
current["_rows"].append(row)
|
30
|
+
|
31
|
+
return result
|
32
|
+
|
33
|
+
def generate_html(self, tree, fold_attributes: List[str]) -> str:
|
34
|
+
html_content = """
|
35
|
+
<!DOCTYPE html>
|
36
|
+
<html lang="en">
|
37
|
+
<head>
|
38
|
+
<meta charset="UTF-8">
|
39
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
40
|
+
<title>Foldable Nested Table</title>
|
41
|
+
<style>
|
42
|
+
.folding-section { margin-left: 20px; }
|
43
|
+
.fold-button { cursor: pointer; margin: 5px 0; }
|
44
|
+
table { border-collapse: collapse; margin-top: 10px; }
|
45
|
+
th, td { border: 1px solid black; padding: 5px; }
|
46
|
+
.attribute-label { font-weight: bold; }
|
47
|
+
</style>
|
48
|
+
</head>
|
49
|
+
<body>
|
50
|
+
<div id="root"></div>
|
51
|
+
<script>
|
52
|
+
function toggleFold(id) {
|
53
|
+
const element = document.getElementById(id);
|
54
|
+
element.style.display = element.style.display === 'none' ? 'block' : 'none';
|
55
|
+
}
|
56
|
+
|
57
|
+
function createFoldableSection(data, path = [], attributes = %s) {
|
58
|
+
const container = document.createElement('div');
|
59
|
+
container.className = 'folding-section';
|
60
|
+
|
61
|
+
for (const [key, value] of Object.entries(data)) {
|
62
|
+
if (key === '_rows') {
|
63
|
+
const table = document.createElement('table');
|
64
|
+
const headerRow = table.insertRow();
|
65
|
+
const headers = Object.keys(value[0]);
|
66
|
+
headers.forEach(header => {
|
67
|
+
const th = document.createElement('th');
|
68
|
+
th.textContent = header;
|
69
|
+
headerRow.appendChild(th);
|
70
|
+
});
|
71
|
+
value.forEach(row => {
|
72
|
+
const tableRow = table.insertRow();
|
73
|
+
headers.forEach(header => {
|
74
|
+
const cell = tableRow.insertCell();
|
75
|
+
cell.textContent = row[header];
|
76
|
+
});
|
77
|
+
});
|
78
|
+
container.appendChild(table);
|
79
|
+
} else {
|
80
|
+
const button = document.createElement('button');
|
81
|
+
const attributeType = attributes[path.length];
|
82
|
+
button.innerHTML = `<span class="attribute-label">${attributeType}:</span> ${key}`;
|
83
|
+
button.className = 'fold-button';
|
84
|
+
const sectionId = `section-${path.join('-')}-${key}`;
|
85
|
+
button.onclick = () => toggleFold(sectionId);
|
86
|
+
container.appendChild(button);
|
87
|
+
|
88
|
+
const section = document.createElement('div');
|
89
|
+
section.id = sectionId;
|
90
|
+
section.style.display = 'none';
|
91
|
+
section.appendChild(createFoldableSection(value, [...path, key], attributes));
|
92
|
+
container.appendChild(section);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
return container;
|
97
|
+
}
|
98
|
+
|
99
|
+
const treeData = %s;
|
100
|
+
document.getElementById('root').appendChild(createFoldableSection(treeData));
|
101
|
+
</script>
|
102
|
+
</body>
|
103
|
+
</html>
|
104
|
+
"""
|
105
|
+
|
106
|
+
return html_content % (json.dumps(fold_attributes), json.dumps(tree))
|
107
|
+
|
108
|
+
def save_html(self, fold_attributes: List[str], filename: str = "output.html"):
|
109
|
+
tree = self.tree(fold_attributes)
|
110
|
+
html_content = self.generate_html(tree, fold_attributes)
|
111
|
+
|
112
|
+
with open(filename, "w", encoding="utf-8") as f:
|
113
|
+
f.write(html_content)
|
114
|
+
|
115
|
+
print(f"HTML file has been generated: {filename}")
|
@@ -0,0 +1,109 @@
|
|
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()
|
@@ -0,0 +1,102 @@
|
|
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()
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class DocxScenario:
|
2
|
+
def __init__(self, docx_path: str):
|
3
|
+
from docx import Document
|
4
|
+
|
5
|
+
self.doc = Document(docx_path)
|
6
|
+
self.docx_path = docx_path
|
7
|
+
|
8
|
+
def get_scenario_dict(self) -> dict:
|
9
|
+
# Extract all text
|
10
|
+
full_text = []
|
11
|
+
for para in self.doc.paragraphs:
|
12
|
+
full_text.append(para.text)
|
13
|
+
|
14
|
+
# Join the text from all paragraphs
|
15
|
+
text = "\n".join(full_text)
|
16
|
+
return {"file_path": self.docx_path, "text": text}
|