edsl 0.1.38.dev4__py3-none-any.whl → 0.1.39__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 +197 -116
- edsl/__init__.py +15 -7
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +351 -147
- edsl/agents/AgentList.py +211 -73
- edsl/agents/Invigilator.py +101 -50
- edsl/agents/InvigilatorBase.py +62 -70
- edsl/agents/PromptConstructor.py +143 -225
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/__init__.py +0 -1
- edsl/agents/prompt_helpers.py +3 -3
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +18 -5
- edsl/auto/StageBase.py +53 -40
- edsl/auto/StageQuestions.py +2 -1
- edsl/auto/utilities.py +0 -6
- edsl/config.py +22 -2
- edsl/conversation/car_buying.py +2 -1
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +1 -1
- edsl/coop/coop.py +125 -47
- edsl/coop/utils.py +14 -14
- edsl/data/Cache.py +45 -27
- edsl/data/CacheEntry.py +12 -15
- edsl/data/CacheHandler.py +31 -12
- edsl/data/RemoteCacheSync.py +154 -46
- edsl/data/__init__.py +4 -3
- edsl/data_transfer_models.py +2 -1
- edsl/enums.py +27 -0
- edsl/exceptions/__init__.py +50 -50
- edsl/exceptions/agents.py +12 -0
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/questions.py +24 -6
- edsl/exceptions/scenarios.py +7 -0
- edsl/inference_services/AnthropicService.py +38 -19
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +0 -2
- edsl/inference_services/AzureAI.py +0 -2
- edsl/inference_services/GoogleService.py +7 -12
- edsl/inference_services/InferenceServiceABC.py +18 -85
- edsl/inference_services/InferenceServicesCollection.py +120 -79
- edsl/inference_services/MistralAIService.py +0 -3
- edsl/inference_services/OpenAIService.py +47 -35
- edsl/inference_services/PerplexityService.py +0 -3
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +11 -10
- edsl/inference_services/TogetherAIService.py +5 -3
- edsl/inference_services/data_structures.py +134 -0
- edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +1 -14
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +356 -431
- edsl/jobs/JobsChecks.py +35 -10
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +6 -4
- edsl/jobs/JobsRemoteInferenceHandler.py +205 -133
- 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 +44 -3
- edsl/jobs/buckets/TokenBucket.py +53 -21
- 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 +143 -408
- 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 +88 -403
- edsl/jobs/runners/JobsRunnerStatus.py +133 -165
- edsl/jobs/tasks/QuestionTaskCreator.py +21 -19
- edsl/jobs/tasks/TaskHistory.py +38 -18
- edsl/jobs/tasks/task_status_enum.py +0 -2
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +194 -236
- edsl/language_models/ModelList.py +28 -19
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/__init__.py +1 -2
- 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 +2 -2
- edsl/language_models/utilities.py +5 -4
- edsl/notebooks/Notebook.py +19 -14
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/prompts/Prompt.py +29 -39
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +68 -214
- edsl/questions/QuestionBasePromptsMixin.py +7 -3
- edsl/questions/QuestionBudget.py +1 -1
- edsl/questions/QuestionCheckBox.py +3 -3
- edsl/questions/QuestionExtract.py +5 -7
- edsl/questions/QuestionFreeText.py +2 -3
- edsl/questions/QuestionList.py +10 -18
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +67 -23
- edsl/questions/QuestionNumerical.py +2 -4
- edsl/questions/QuestionRank.py +7 -17
- edsl/questions/SimpleAskMixin.py +4 -3
- edsl/questions/__init__.py +2 -1
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +47 -2
- edsl/questions/data_structures.py +20 -0
- edsl/questions/derived/QuestionLinearScale.py +6 -3
- edsl/questions/derived/QuestionTopK.py +1 -1
- edsl/questions/descriptors.py +17 -3
- edsl/questions/loop_processor.py +149 -0
- edsl/questions/{QuestionBaseGenMixin.py → question_base_gen_mixin.py} +57 -50
- edsl/questions/question_registry.py +1 -1
- edsl/questions/{ResponseValidatorABC.py → response_validator_abc.py} +40 -26
- edsl/questions/response_validator_factory.py +34 -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/results/CSSParameterizer.py +1 -1
- edsl/results/Dataset.py +170 -7
- edsl/results/DatasetExportMixin.py +168 -305
- edsl/results/DatasetTree.py +28 -8
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +298 -206
- edsl/results/Results.py +149 -131
- edsl/results/ResultsExportMixin.py +2 -0
- edsl/results/TableDisplay.py +98 -171
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +1 -1
- edsl/results/file_exports.py +252 -0
- edsl/results/{Selector.py → results_selector.py} +23 -13
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_renderers.py +118 -0
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +150 -239
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +90 -193
- edsl/scenarios/ScenarioHtmlMixin.py +4 -3
- edsl/scenarios/ScenarioList.py +415 -244
- edsl/scenarios/ScenarioListExportMixin.py +0 -7
- edsl/scenarios/ScenarioListPdfMixin.py +15 -37
- edsl/scenarios/__init__.py +1 -2
- 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/{ScenarioJoin.py → scenario_join.py} +10 -6
- edsl/scenarios/scenario_selector.py +156 -0
- edsl/study/ObjectEntry.py +1 -1
- edsl/study/SnapShot.py +1 -1
- edsl/study/Study.py +5 -12
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/Rule.py +5 -4
- edsl/surveys/RuleCollection.py +25 -27
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +270 -791
- edsl/surveys/SurveyCSS.py +20 -8
- edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +11 -9
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +4 -2
- edsl/surveys/descriptors.py +6 -2
- edsl/surveys/instructions/ChangeInstruction.py +1 -2
- edsl/surveys/instructions/Instruction.py +4 -13
- edsl/surveys/instructions/InstructionCollection.py +11 -6
- edsl/templates/error_reporting/interview_details.html +1 -1
- edsl/templates/error_reporting/report.html +1 -1
- edsl/tools/plotting.py +1 -1
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/utilities.py +35 -23
- {edsl-0.1.38.dev4.dist-info → edsl-0.1.39.dist-info}/METADATA +12 -10
- edsl-0.1.39.dist-info/RECORD +358 -0
- {edsl-0.1.38.dev4.dist-info → edsl-0.1.39.dist-info}/WHEEL +1 -1
- edsl/language_models/KeyLookup.py +0 -30
- edsl/language_models/registry.py +0 -190
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/results/ResultsDBMixin.py +0 -238
- edsl-0.1.38.dev4.dist-info/RECORD +0 -277
- /edsl/questions/{RegisterQuestionsMeta.py → register_questions_meta.py} +0 -0
- /edsl/results/{ResultsFetchMixin.py → results_fetch_mixin.py} +0 -0
- /edsl/results/{ResultsToolsMixin.py → results_tools_mixin.py} +0 -0
- {edsl-0.1.38.dev4.dist-info → edsl-0.1.39.dist-info}/LICENSE +0 -0
edsl/prompts/Prompt.py
CHANGED
@@ -1,43 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Optional
|
3
|
-
from abc import ABC
|
4
|
-
from typing import Any, List
|
5
|
-
|
6
|
-
from jinja2 import Environment, FileSystemLoader
|
7
|
-
from typing import Union, Dict
|
2
|
+
from typing import Any, List, Union, Dict, Optional
|
8
3
|
from pathlib import Path
|
9
4
|
|
10
|
-
from
|
11
|
-
from jinja2 import Template, Environment, meta, TemplateSyntaxError, Undefined
|
12
|
-
|
13
|
-
|
14
|
-
class PreserveUndefined(Undefined):
|
15
|
-
def __str__(self):
|
16
|
-
return "{{ " + str(self._undefined_name) + " }}"
|
5
|
+
# from jinja2 import Undefined
|
17
6
|
|
18
7
|
|
19
8
|
from edsl.exceptions.prompts import TemplateRenderError
|
20
|
-
from edsl.Base import PersistenceMixin,
|
9
|
+
from edsl.Base import PersistenceMixin, RepresentationMixin
|
21
10
|
|
22
11
|
MAX_NESTING = 100
|
23
12
|
|
24
13
|
|
25
|
-
class Prompt(PersistenceMixin,
|
14
|
+
class Prompt(PersistenceMixin, RepresentationMixin):
|
26
15
|
"""Class for creating a prompt to be used in a survey."""
|
27
16
|
|
28
17
|
default_instructions: Optional[str] = "Do good things, friendly LLM!"
|
29
18
|
|
30
|
-
def _repr_html_(self):
|
31
|
-
"""Return an HTML representation of the Prompt."""
|
32
|
-
# from edsl.utilities.utilities import data_to_html
|
33
|
-
# return data_to_html(self.to_dict())
|
34
|
-
d = self.to_dict()
|
35
|
-
data = [[k, v] for k, v in d.items()]
|
36
|
-
from tabulate import tabulate
|
37
|
-
|
38
|
-
table = str(tabulate(data, headers=["keys", "values"], tablefmt="html"))
|
39
|
-
return f"<pre>{table}</pre>"
|
40
|
-
|
41
19
|
def __len__(self):
|
42
20
|
"""Return the length of the prompt text."""
|
43
21
|
return len(self.text)
|
@@ -185,6 +163,12 @@ class Prompt(PersistenceMixin, RichPrintingMixin):
|
|
185
163
|
:param template: The template to find the variables in.
|
186
164
|
|
187
165
|
"""
|
166
|
+
from jinja2 import Environment, meta, Undefined
|
167
|
+
|
168
|
+
class PreserveUndefined(Undefined):
|
169
|
+
def __str__(self):
|
170
|
+
return "{{ " + str(self._undefined_name) + " }}"
|
171
|
+
|
188
172
|
env = Environment(undefined=PreserveUndefined)
|
189
173
|
ast = env.parse(template)
|
190
174
|
return list(meta.find_undeclared_variables(ast))
|
@@ -273,6 +257,12 @@ class Prompt(PersistenceMixin, RichPrintingMixin):
|
|
273
257
|
>>> p.render({"name": "John", "age": 44}, codebook=codebook)
|
274
258
|
Prompt(text=\"""You are an agent named John. Age: 44\""")
|
275
259
|
"""
|
260
|
+
from jinja2 import Environment, meta, TemplateSyntaxError, Undefined
|
261
|
+
|
262
|
+
class PreserveUndefined(Undefined):
|
263
|
+
def __str__(self):
|
264
|
+
return "{{ " + str(self._undefined_name) + " }}"
|
265
|
+
|
276
266
|
env = Environment(undefined=PreserveUndefined)
|
277
267
|
try:
|
278
268
|
previous_text = None
|
@@ -296,7 +286,7 @@ class Prompt(PersistenceMixin, RichPrintingMixin):
|
|
296
286
|
f"Template syntax error: {e}. Bad template: {text}"
|
297
287
|
)
|
298
288
|
|
299
|
-
def to_dict(self) -> dict[str, Any]:
|
289
|
+
def to_dict(self, add_edsl_version=False) -> dict[str, Any]:
|
300
290
|
"""Return the `Prompt` as a dictionary.
|
301
291
|
|
302
292
|
Example:
|
@@ -323,18 +313,18 @@ class Prompt(PersistenceMixin, RichPrintingMixin):
|
|
323
313
|
# class_name = data["class_name"]
|
324
314
|
return Prompt(text=data["text"])
|
325
315
|
|
326
|
-
def rich_print(self):
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
316
|
+
# def rich_print(self):
|
317
|
+
# """Display an object as a table."""
|
318
|
+
# table = Table(title="Prompt")
|
319
|
+
# table.add_column("Attribute", style="bold")
|
320
|
+
# table.add_column("Value")
|
321
|
+
|
322
|
+
# to_display = self.__dict__.copy()
|
323
|
+
# for attr_name, attr_value in to_display.items():
|
324
|
+
# table.add_row(attr_name, repr(attr_value))
|
325
|
+
# table.add_row("Component type", str(self.component_type))
|
326
|
+
# table.add_row("Model", str(getattr(self, "model", "Not specified")))
|
327
|
+
# return table
|
338
328
|
|
339
329
|
@classmethod
|
340
330
|
def example(cls):
|
@@ -0,0 +1,77 @@
|
|
1
|
+
from pydantic import ValidationError
|
2
|
+
from typing import Union
|
3
|
+
|
4
|
+
|
5
|
+
class ExceptionExplainer:
|
6
|
+
"""
|
7
|
+
A class that converts validation errors into human-readable explanations,
|
8
|
+
specifically for Language Model responses.
|
9
|
+
"""
|
10
|
+
|
11
|
+
def __init__(self, error: Union[ValidationError, Exception], model_response: str):
|
12
|
+
"""
|
13
|
+
Initialize the explainer with the error and model response.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
error: The validation error that occurred
|
17
|
+
model_response: The raw response from the Language Model
|
18
|
+
"""
|
19
|
+
self.error = error
|
20
|
+
self.model_response = model_response
|
21
|
+
|
22
|
+
def explain(self) -> str:
|
23
|
+
"""
|
24
|
+
Generate a human-readable explanation of why the model's response failed validation.
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
A user-friendly explanation of why the model's response was invalid
|
28
|
+
"""
|
29
|
+
self.error = self.error.pydantic_error
|
30
|
+
return self._explain_validation_error()
|
31
|
+
|
32
|
+
# Fallback for unknown errors
|
33
|
+
return self._create_generic_explanation()
|
34
|
+
|
35
|
+
def _explain_validation_error(self) -> str:
|
36
|
+
"""Handle Pydantic ValidationError specifically."""
|
37
|
+
error_dict = self.error.errors()
|
38
|
+
explanations = []
|
39
|
+
|
40
|
+
context = f'The AI model returned "{self.model_response}", but this was invalid for the question you asked and the constraints you provided.\n'
|
41
|
+
explanations.append(context)
|
42
|
+
explanations.append("Reason(s) invalidated:")
|
43
|
+
for e in error_dict:
|
44
|
+
msg = e.get("msg", "Unknown error")
|
45
|
+
explanations.append(f"- {msg}")
|
46
|
+
|
47
|
+
main_message = "\n".join(explanations)
|
48
|
+
return f"{main_message}\n\n{self._get_suggestion()}"
|
49
|
+
|
50
|
+
def _create_generic_explanation(self) -> str:
|
51
|
+
"""Create a generic explanation for non-ValidationError exceptions."""
|
52
|
+
return (
|
53
|
+
f'The AI model returned "{self.model_response}", but this response was invalid. '
|
54
|
+
f"Error: {str(self.error)}"
|
55
|
+
)
|
56
|
+
|
57
|
+
def _get_suggestion(self) -> str:
|
58
|
+
"""Get a suggestion for handling the error."""
|
59
|
+
return (
|
60
|
+
"EDSL Advice:\n"
|
61
|
+
"- Look at the Model comments - often the model will provide a hint about what went wrong.\n"
|
62
|
+
"- If the model's response doesn't make sense, try rephrasing your question.\n"
|
63
|
+
"- Try using 'use_code' parameter of a MultipleChoice.\n"
|
64
|
+
"- A QuestionFreeText will almost always validate.\n"
|
65
|
+
"- Try setting the 'permissive' = True parameter in the Question constructor."
|
66
|
+
)
|
67
|
+
|
68
|
+
|
69
|
+
# Example usage:
|
70
|
+
if __name__ == "__main__":
|
71
|
+
try:
|
72
|
+
# Your validation code here
|
73
|
+
raise ValidationError.parse_obj({"answer": "120"})
|
74
|
+
except ValidationError as e:
|
75
|
+
explainer = ExceptionExplainer(e, "120")
|
76
|
+
explanation = explainer.explain()
|
77
|
+
print(explanation)
|
@@ -0,0 +1,103 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
from edsl.prompts.Prompt import Prompt
|
3
|
+
|
4
|
+
|
5
|
+
class HTMLQuestion:
|
6
|
+
def __init__(self, question):
|
7
|
+
self.question = question
|
8
|
+
|
9
|
+
def html(
|
10
|
+
self,
|
11
|
+
scenario: Optional[dict] = None,
|
12
|
+
agent: Optional[dict] = {},
|
13
|
+
answers: Optional[dict] = None,
|
14
|
+
include_question_name: bool = False,
|
15
|
+
height: Optional[int] = None,
|
16
|
+
width: Optional[int] = None,
|
17
|
+
iframe=False,
|
18
|
+
):
|
19
|
+
"""Return the question in HTML format."""
|
20
|
+
from jinja2 import Template
|
21
|
+
|
22
|
+
if scenario is None:
|
23
|
+
scenario = {}
|
24
|
+
|
25
|
+
prior_answers_dict = {}
|
26
|
+
|
27
|
+
if isinstance(answers, dict):
|
28
|
+
for key, value in answers.items():
|
29
|
+
if not key.endswith("_comment") and not key.endswith(
|
30
|
+
"_generated_tokens"
|
31
|
+
):
|
32
|
+
prior_answers_dict[key] = {"answer": value}
|
33
|
+
|
34
|
+
base_template = """
|
35
|
+
<div id="{{ question_name }}" class="survey_question" data-type="{{ question_type }}">
|
36
|
+
{% if include_question_name %}
|
37
|
+
<p>question_name: {{ question_name }}</p>
|
38
|
+
{% endif %}
|
39
|
+
<p class="question_text">{{ question_text }}</p>
|
40
|
+
{{ question_content }}
|
41
|
+
</div>
|
42
|
+
"""
|
43
|
+
if not hasattr(self.question, "question_type"):
|
44
|
+
self.question.question_type = "unknown"
|
45
|
+
|
46
|
+
if hasattr(self.question, "question_html_content"):
|
47
|
+
question_content = self.question.question_html_content
|
48
|
+
else:
|
49
|
+
question_content = Template("")
|
50
|
+
|
51
|
+
base_template = Template(base_template)
|
52
|
+
|
53
|
+
context = {
|
54
|
+
"scenario": scenario,
|
55
|
+
"agent": agent,
|
56
|
+
} | prior_answers_dict
|
57
|
+
|
58
|
+
# Render the question text
|
59
|
+
try:
|
60
|
+
question_text = Template(self.question.question_text).render(context)
|
61
|
+
except Exception as e:
|
62
|
+
print(
|
63
|
+
f"Error rendering question: question_text = {self.question.question_text}, error = {e}"
|
64
|
+
)
|
65
|
+
question_text = self.question.question_text
|
66
|
+
|
67
|
+
try:
|
68
|
+
question_content = Template(question_content).render(context)
|
69
|
+
except Exception as e:
|
70
|
+
print(
|
71
|
+
f"Error rendering question: question_content = {question_content}, error = {e}"
|
72
|
+
)
|
73
|
+
question_content = question_content
|
74
|
+
|
75
|
+
try:
|
76
|
+
params = {
|
77
|
+
"question_name": self.question.question_name,
|
78
|
+
"question_text": question_text,
|
79
|
+
"question_type": self.question.question_type,
|
80
|
+
"question_content": question_content,
|
81
|
+
"include_question_name": include_question_name,
|
82
|
+
}
|
83
|
+
except Exception as e:
|
84
|
+
raise ValueError(
|
85
|
+
f"Error rendering question: params = {params}, error = {e}"
|
86
|
+
)
|
87
|
+
rendered_html = base_template.render(**params)
|
88
|
+
|
89
|
+
if iframe:
|
90
|
+
import html
|
91
|
+
from IPython.display import display, HTML
|
92
|
+
|
93
|
+
height = height or 200
|
94
|
+
width = width or 600
|
95
|
+
escaped_output = html.escape(rendered_html)
|
96
|
+
# escaped_output = rendered_html
|
97
|
+
iframe = f""""
|
98
|
+
<iframe srcdoc="{ escaped_output }" style="width: {width}px; height: {height}px;"></iframe>
|
99
|
+
"""
|
100
|
+
display(HTML(iframe))
|
101
|
+
return None
|
102
|
+
|
103
|
+
return rendered_html
|