edsl 0.1.14__py3-none-any.whl → 0.1.40__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edsl/Base.py +348 -38
- edsl/BaseDiff.py +260 -0
- edsl/TemplateLoader.py +24 -0
- edsl/__init__.py +46 -10
- edsl/__version__.py +1 -0
- edsl/agents/Agent.py +842 -144
- edsl/agents/AgentList.py +521 -25
- edsl/agents/Invigilator.py +250 -374
- edsl/agents/InvigilatorBase.py +257 -0
- edsl/agents/PromptConstructor.py +272 -0
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/descriptors.py +43 -13
- edsl/agents/prompt_helpers.py +129 -0
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +130 -0
- edsl/auto/StageBase.py +243 -0
- edsl/auto/StageGenerateSurvey.py +178 -0
- edsl/auto/StageLabelQuestions.py +125 -0
- edsl/auto/StagePersona.py +61 -0
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
- edsl/auto/StagePersonaDimensionValues.py +74 -0
- edsl/auto/StagePersonaDimensions.py +69 -0
- edsl/auto/StageQuestions.py +74 -0
- edsl/auto/SurveyCreatorPipeline.py +21 -0
- edsl/auto/utilities.py +218 -0
- edsl/base/Base.py +279 -0
- edsl/config.py +121 -104
- edsl/conversation/Conversation.py +290 -0
- edsl/conversation/car_buying.py +59 -0
- edsl/conversation/chips.py +95 -0
- edsl/conversation/mug_negotiation.py +81 -0
- edsl/conversation/next_speaker_utilities.py +93 -0
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +54 -0
- edsl/coop/__init__.py +1 -0
- edsl/coop/coop.py +1029 -134
- edsl/coop/utils.py +131 -0
- edsl/data/Cache.py +560 -89
- edsl/data/CacheEntry.py +230 -0
- edsl/data/CacheHandler.py +168 -0
- edsl/data/RemoteCacheSync.py +186 -0
- edsl/data/SQLiteDict.py +292 -0
- edsl/data/__init__.py +5 -3
- edsl/data/orm.py +6 -33
- edsl/data_transfer_models.py +74 -27
- edsl/enums.py +165 -8
- edsl/exceptions/BaseException.py +21 -0
- edsl/exceptions/__init__.py +52 -46
- edsl/exceptions/agents.py +33 -15
- edsl/exceptions/cache.py +5 -0
- edsl/exceptions/coop.py +8 -0
- edsl/exceptions/general.py +34 -0
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/jobs.py +15 -0
- edsl/exceptions/language_models.py +46 -1
- edsl/exceptions/questions.py +80 -5
- edsl/exceptions/results.py +16 -5
- edsl/exceptions/scenarios.py +29 -0
- edsl/exceptions/surveys.py +13 -10
- edsl/inference_services/AnthropicService.py +106 -0
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +118 -0
- edsl/inference_services/AzureAI.py +215 -0
- edsl/inference_services/DeepInfraService.py +18 -0
- edsl/inference_services/GoogleService.py +143 -0
- edsl/inference_services/GroqService.py +20 -0
- edsl/inference_services/InferenceServiceABC.py +80 -0
- edsl/inference_services/InferenceServicesCollection.py +138 -0
- edsl/inference_services/MistralAIService.py +120 -0
- edsl/inference_services/OllamaService.py +18 -0
- edsl/inference_services/OpenAIService.py +236 -0
- edsl/inference_services/PerplexityService.py +160 -0
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +90 -0
- edsl/inference_services/TogetherAIService.py +172 -0
- edsl/inference_services/data_structures.py +134 -0
- edsl/inference_services/models_available_cache.py +118 -0
- edsl/inference_services/rate_limits_cache.py +25 -0
- edsl/inference_services/registry.py +41 -0
- edsl/inference_services/write_available.py +10 -0
- edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +21 -20
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +684 -204
- edsl/jobs/JobsChecks.py +172 -0
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +270 -0
- edsl/jobs/JobsRemoteInferenceHandler.py +311 -0
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/async_interview_runner.py +138 -0
- edsl/jobs/buckets/BucketCollection.py +104 -0
- edsl/jobs/buckets/ModelBuckets.py +65 -0
- edsl/jobs/buckets/TokenBucket.py +283 -0
- edsl/jobs/buckets/TokenBucketAPI.py +211 -0
- edsl/jobs/buckets/TokenBucketClient.py +191 -0
- edsl/jobs/check_survey_scenario_compatibility.py +85 -0
- edsl/jobs/data_structures.py +120 -0
- edsl/jobs/decorators.py +35 -0
- edsl/jobs/interviews/Interview.py +392 -0
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -0
- edsl/jobs/interviews/InterviewExceptionEntry.py +186 -0
- edsl/jobs/interviews/InterviewStatistic.py +63 -0
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -0
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -0
- edsl/jobs/interviews/InterviewStatusLog.py +92 -0
- edsl/jobs/interviews/ReportErrors.py +66 -0
- edsl/jobs/interviews/interview_status_enum.py +9 -0
- edsl/jobs/jobs_status_enums.py +9 -0
- edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
- edsl/jobs/results_exceptions_handler.py +98 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +151 -110
- edsl/jobs/runners/JobsRunnerStatus.py +298 -0
- edsl/jobs/tasks/QuestionTaskCreator.py +244 -0
- edsl/jobs/tasks/TaskCreators.py +64 -0
- edsl/jobs/tasks/TaskHistory.py +470 -0
- edsl/jobs/tasks/TaskStatusLog.py +23 -0
- edsl/jobs/tasks/task_status_enum.py +161 -0
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -0
- edsl/jobs/tokens/TokenUsage.py +34 -0
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +507 -386
- edsl/language_models/ModelList.py +164 -0
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -0
- edsl/language_models/__init__.py +1 -8
- edsl/language_models/fake_openai_call.py +15 -0
- edsl/language_models/fake_openai_service.py +61 -0
- edsl/language_models/key_management/KeyLookup.py +63 -0
- edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
- edsl/language_models/key_management/KeyLookupCollection.py +38 -0
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +131 -0
- edsl/language_models/model.py +256 -0
- edsl/language_models/repair.py +109 -41
- edsl/language_models/utilities.py +65 -0
- edsl/notebooks/Notebook.py +263 -0
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/notebooks/__init__.py +1 -0
- edsl/prompts/Prompt.py +222 -93
- edsl/prompts/__init__.py +1 -1
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +518 -0
- edsl/questions/QuestionBasePromptsMixin.py +221 -0
- edsl/questions/QuestionBudget.py +164 -67
- edsl/questions/QuestionCheckBox.py +281 -62
- edsl/questions/QuestionDict.py +343 -0
- edsl/questions/QuestionExtract.py +136 -50
- edsl/questions/QuestionFreeText.py +79 -55
- edsl/questions/QuestionFunctional.py +138 -41
- edsl/questions/QuestionList.py +184 -57
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +293 -69
- edsl/questions/QuestionNumerical.py +109 -56
- edsl/questions/QuestionRank.py +244 -49
- edsl/questions/Quick.py +41 -0
- edsl/questions/SimpleAskMixin.py +74 -0
- edsl/questions/__init__.py +9 -6
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +153 -38
- edsl/questions/compose_questions.py +13 -7
- edsl/questions/data_structures.py +20 -0
- edsl/questions/decorators.py +21 -0
- edsl/questions/derived/QuestionLikertFive.py +28 -26
- edsl/questions/derived/QuestionLinearScale.py +41 -28
- edsl/questions/derived/QuestionTopK.py +34 -26
- edsl/questions/derived/QuestionYesNo.py +40 -27
- edsl/questions/descriptors.py +228 -74
- edsl/questions/loop_processor.py +149 -0
- edsl/questions/prompt_templates/question_budget.jinja +13 -0
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
- edsl/questions/prompt_templates/question_extract.jinja +11 -0
- edsl/questions/prompt_templates/question_free_text.jinja +3 -0
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
- edsl/questions/prompt_templates/question_list.jinja +17 -0
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
- edsl/questions/prompt_templates/question_numerical.jinja +37 -0
- edsl/questions/question_base_gen_mixin.py +168 -0
- edsl/questions/question_registry.py +130 -46
- edsl/questions/register_questions_meta.py +71 -0
- edsl/questions/response_validator_abc.py +188 -0
- edsl/questions/response_validator_factory.py +34 -0
- edsl/questions/settings.py +5 -2
- edsl/questions/templates/__init__.py +0 -0
- edsl/questions/templates/budget/__init__.py +0 -0
- edsl/questions/templates/budget/answering_instructions.jinja +7 -0
- edsl/questions/templates/budget/question_presentation.jinja +7 -0
- edsl/questions/templates/checkbox/__init__.py +0 -0
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
- edsl/questions/templates/dict/__init__.py +0 -0
- edsl/questions/templates/dict/answering_instructions.jinja +21 -0
- edsl/questions/templates/dict/question_presentation.jinja +1 -0
- edsl/questions/templates/extract/__init__.py +0 -0
- edsl/questions/templates/extract/answering_instructions.jinja +7 -0
- edsl/questions/templates/extract/question_presentation.jinja +1 -0
- edsl/questions/templates/free_text/__init__.py +0 -0
- edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
- edsl/questions/templates/free_text/question_presentation.jinja +1 -0
- edsl/questions/templates/likert_five/__init__.py +0 -0
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
- edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
- edsl/questions/templates/linear_scale/__init__.py +0 -0
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
- edsl/questions/templates/list/__init__.py +0 -0
- edsl/questions/templates/list/answering_instructions.jinja +4 -0
- edsl/questions/templates/list/question_presentation.jinja +5 -0
- edsl/questions/templates/matrix/__init__.py +1 -0
- edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
- edsl/questions/templates/matrix/question_presentation.jinja +20 -0
- edsl/questions/templates/multiple_choice/__init__.py +0 -0
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
- edsl/questions/templates/multiple_choice/html.jinja +0 -0
- edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
- edsl/questions/templates/numerical/__init__.py +0 -0
- edsl/questions/templates/numerical/answering_instructions.jinja +7 -0
- edsl/questions/templates/numerical/question_presentation.jinja +7 -0
- edsl/questions/templates/rank/__init__.py +0 -0
- edsl/questions/templates/rank/answering_instructions.jinja +11 -0
- edsl/questions/templates/rank/question_presentation.jinja +15 -0
- edsl/questions/templates/top_k/__init__.py +0 -0
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
- edsl/questions/templates/top_k/question_presentation.jinja +22 -0
- edsl/questions/templates/yes_no/__init__.py +0 -0
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
- edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
- edsl/results/CSSParameterizer.py +108 -0
- edsl/results/Dataset.py +550 -19
- edsl/results/DatasetExportMixin.py +594 -0
- edsl/results/DatasetTree.py +295 -0
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +477 -173
- edsl/results/Results.py +987 -269
- edsl/results/ResultsExportMixin.py +28 -125
- edsl/results/ResultsGGMixin.py +83 -15
- edsl/results/TableDisplay.py +125 -0
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +1 -1
- edsl/results/file_exports.py +252 -0
- edsl/results/results_fetch_mixin.py +33 -0
- edsl/results/results_selector.py +145 -0
- edsl/results/results_tools_mixin.py +98 -0
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_display.css +78 -0
- edsl/results/table_renderers.py +118 -0
- edsl/results/tree_explore.py +115 -0
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +543 -0
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +431 -62
- edsl/scenarios/ScenarioHtmlMixin.py +65 -0
- edsl/scenarios/ScenarioList.py +1415 -45
- edsl/scenarios/ScenarioListExportMixin.py +45 -0
- edsl/scenarios/ScenarioListPdfMixin.py +239 -0
- edsl/scenarios/__init__.py +2 -0
- edsl/scenarios/directory_scanner.py +96 -0
- edsl/scenarios/file_methods.py +85 -0
- edsl/scenarios/handlers/__init__.py +13 -0
- edsl/scenarios/handlers/csv.py +49 -0
- edsl/scenarios/handlers/docx.py +76 -0
- edsl/scenarios/handlers/html.py +37 -0
- edsl/scenarios/handlers/json.py +111 -0
- edsl/scenarios/handlers/latex.py +5 -0
- edsl/scenarios/handlers/md.py +51 -0
- edsl/scenarios/handlers/pdf.py +68 -0
- edsl/scenarios/handlers/png.py +39 -0
- edsl/scenarios/handlers/pptx.py +105 -0
- edsl/scenarios/handlers/py.py +294 -0
- edsl/scenarios/handlers/sql.py +313 -0
- edsl/scenarios/handlers/sqlite.py +149 -0
- edsl/scenarios/handlers/txt.py +33 -0
- edsl/scenarios/scenario_join.py +131 -0
- edsl/scenarios/scenario_selector.py +156 -0
- edsl/shared.py +1 -0
- edsl/study/ObjectEntry.py +173 -0
- edsl/study/ProofOfWork.py +113 -0
- edsl/study/SnapShot.py +80 -0
- edsl/study/Study.py +521 -0
- edsl/study/__init__.py +4 -0
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/DAG.py +92 -11
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/Memory.py +9 -4
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/MemoryPlan.py +156 -35
- edsl/surveys/Rule.py +221 -74
- edsl/surveys/RuleCollection.py +241 -61
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +1079 -339
- edsl/surveys/SurveyCSS.py +273 -0
- edsl/surveys/SurveyExportMixin.py +235 -40
- edsl/surveys/SurveyFlowVisualization.py +181 -0
- edsl/surveys/SurveyQualtricsImport.py +284 -0
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +4 -2
- edsl/surveys/base.py +19 -3
- edsl/surveys/descriptors.py +17 -6
- edsl/surveys/instructions/ChangeInstruction.py +48 -0
- edsl/surveys/instructions/Instruction.py +56 -0
- edsl/surveys/instructions/InstructionCollection.py +82 -0
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/templates/error_reporting/base.html +24 -0
- edsl/templates/error_reporting/exceptions_by_model.html +35 -0
- edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
- edsl/templates/error_reporting/exceptions_by_type.html +17 -0
- edsl/templates/error_reporting/interview_details.html +116 -0
- edsl/templates/error_reporting/interviews.html +19 -0
- edsl/templates/error_reporting/overview.html +5 -0
- edsl/templates/error_reporting/performance_plot.html +2 -0
- edsl/templates/error_reporting/report.css +74 -0
- edsl/templates/error_reporting/report.html +118 -0
- edsl/templates/error_reporting/report.js +25 -0
- edsl/tools/__init__.py +1 -0
- edsl/tools/clusters.py +192 -0
- edsl/tools/embeddings.py +27 -0
- edsl/tools/embeddings_plotting.py +118 -0
- edsl/tools/plotting.py +112 -0
- edsl/tools/summarize.py +18 -0
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/SystemInfo.py +5 -0
- edsl/utilities/__init__.py +21 -20
- edsl/utilities/ast_utilities.py +3 -0
- edsl/utilities/data/Registry.py +2 -0
- edsl/utilities/decorators.py +41 -0
- edsl/utilities/gcp_bucket/__init__.py +0 -0
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -0
- edsl/utilities/interface.py +310 -60
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/naming_utilities.py +263 -0
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/repair_functions.py +28 -0
- edsl/utilities/restricted_python.py +70 -0
- edsl/utilities/utilities.py +203 -13
- edsl-0.1.40.dist-info/METADATA +111 -0
- edsl-0.1.40.dist-info/RECORD +362 -0
- {edsl-0.1.14.dist-info → edsl-0.1.40.dist-info}/WHEEL +1 -1
- edsl/agents/AgentListExportMixin.py +0 -24
- edsl/coop/old.py +0 -31
- edsl/data/Database.py +0 -141
- edsl/data/crud.py +0 -121
- edsl/jobs/Interview.py +0 -417
- edsl/jobs/JobsRunner.py +0 -63
- edsl/jobs/JobsRunnerStatusMixin.py +0 -115
- edsl/jobs/base.py +0 -47
- edsl/jobs/buckets.py +0 -166
- edsl/jobs/runners/JobsRunnerDryRun.py +0 -19
- edsl/jobs/runners/JobsRunnerStreaming.py +0 -54
- edsl/jobs/task_management.py +0 -218
- edsl/jobs/token_tracking.py +0 -78
- edsl/language_models/DeepInfra.py +0 -69
- edsl/language_models/OpenAI.py +0 -98
- edsl/language_models/model_interfaces/GeminiPro.py +0 -66
- edsl/language_models/model_interfaces/LanguageModelOpenAIFour.py +0 -8
- edsl/language_models/model_interfaces/LanguageModelOpenAIThreeFiveTurbo.py +0 -8
- edsl/language_models/model_interfaces/LlamaTwo13B.py +0 -21
- edsl/language_models/model_interfaces/LlamaTwo70B.py +0 -21
- edsl/language_models/model_interfaces/Mixtral8x7B.py +0 -24
- edsl/language_models/registry.py +0 -81
- edsl/language_models/schemas.py +0 -15
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/prompts/QuestionInstructionsBase.py +0 -6
- edsl/prompts/library/agent_instructions.py +0 -29
- edsl/prompts/library/agent_persona.py +0 -17
- edsl/prompts/library/question_budget.py +0 -26
- edsl/prompts/library/question_checkbox.py +0 -32
- edsl/prompts/library/question_extract.py +0 -19
- edsl/prompts/library/question_freetext.py +0 -14
- edsl/prompts/library/question_linear_scale.py +0 -20
- edsl/prompts/library/question_list.py +0 -22
- edsl/prompts/library/question_multiple_choice.py +0 -44
- edsl/prompts/library/question_numerical.py +0 -31
- edsl/prompts/library/question_rank.py +0 -21
- edsl/prompts/prompt_config.py +0 -33
- edsl/prompts/registry.py +0 -185
- edsl/questions/Question.py +0 -240
- edsl/report/InputOutputDataTypes.py +0 -134
- edsl/report/RegressionMixin.py +0 -28
- edsl/report/ReportOutputs.py +0 -1228
- edsl/report/ResultsFetchMixin.py +0 -106
- edsl/report/ResultsOutputMixin.py +0 -14
- edsl/report/demo.ipynb +0 -645
- edsl/results/ResultsDBMixin.py +0 -184
- edsl/surveys/SurveyFlowVisualizationMixin.py +0 -92
- edsl/trackers/Tracker.py +0 -91
- edsl/trackers/TrackerAPI.py +0 -196
- edsl/trackers/TrackerTasks.py +0 -70
- edsl/utilities/pastebin.py +0 -141
- edsl-0.1.14.dist-info/METADATA +0 -69
- edsl-0.1.14.dist-info/RECORD +0 -141
- /edsl/{language_models/model_interfaces → inference_services}/__init__.py +0 -0
- /edsl/{report/__init__.py → jobs/runners/JobsRunnerStatusData.py} +0 -0
- /edsl/{trackers/__init__.py → language_models/ServiceDataSources.py} +0 -0
- {edsl-0.1.14.dist-info → edsl-0.1.40.dist-info}/LICENSE +0 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
"""A mixin for visualizing the flow of a survey with parameter nodes."""
|
2
|
+
|
3
|
+
from typing import Optional, TYPE_CHECKING
|
4
|
+
from edsl.surveys.base import RulePriority, EndOfSurvey
|
5
|
+
import tempfile
|
6
|
+
|
7
|
+
|
8
|
+
class SurveyFlowVisualization:
|
9
|
+
"""A mixin for visualizing the flow of a survey with parameter visualization."""
|
10
|
+
|
11
|
+
def __init__(self, survey: "Survey"):
|
12
|
+
self.survey = survey
|
13
|
+
|
14
|
+
def show_flow(self, filename: Optional[str] = None):
|
15
|
+
"""Create an image showing the flow of users through the survey and question parameters."""
|
16
|
+
# Create a graph object
|
17
|
+
import pydot
|
18
|
+
|
19
|
+
graph = pydot.Dot(graph_type="digraph")
|
20
|
+
|
21
|
+
# First collect all unique parameters and answer references
|
22
|
+
params_and_refs = set()
|
23
|
+
param_to_questions = {} # Keep track of which questions use each parameter
|
24
|
+
answer_refs = set() # Track answer references between questions
|
25
|
+
|
26
|
+
# First pass: collect parameters and their question associations
|
27
|
+
for index, question in enumerate(self.survey.questions):
|
28
|
+
# Add the main question node
|
29
|
+
question_node = pydot.Node(
|
30
|
+
f"Q{index}", label=f"{question.question_name}", shape="ellipse"
|
31
|
+
)
|
32
|
+
graph.add_node(question_node)
|
33
|
+
|
34
|
+
if hasattr(question, "parameters"):
|
35
|
+
for param in question.parameters:
|
36
|
+
# Check if this is an answer reference (contains '.answer')
|
37
|
+
if ".answer" in param:
|
38
|
+
answer_refs.add((param.split(".")[0], index))
|
39
|
+
else:
|
40
|
+
params_and_refs.add(param)
|
41
|
+
if param not in param_to_questions:
|
42
|
+
param_to_questions[param] = []
|
43
|
+
param_to_questions[param].append(index)
|
44
|
+
|
45
|
+
# Create parameter nodes and connect them to questions
|
46
|
+
for param in params_and_refs:
|
47
|
+
param_node_name = f"param_{param}"
|
48
|
+
param_node = pydot.Node(
|
49
|
+
param_node_name,
|
50
|
+
label=f"{{{{ {param} }}}}",
|
51
|
+
shape="box",
|
52
|
+
style="filled",
|
53
|
+
fillcolor="lightgrey",
|
54
|
+
fontsize="10",
|
55
|
+
)
|
56
|
+
graph.add_node(param_node)
|
57
|
+
|
58
|
+
# Connect this parameter to all questions that use it
|
59
|
+
for q_index in param_to_questions[param]:
|
60
|
+
param_edge = pydot.Edge(
|
61
|
+
param_node_name,
|
62
|
+
f"Q{q_index}",
|
63
|
+
style="dotted",
|
64
|
+
color="grey",
|
65
|
+
arrowsize="0.5",
|
66
|
+
)
|
67
|
+
graph.add_edge(param_edge)
|
68
|
+
|
69
|
+
# Add edges for answer references
|
70
|
+
for source_q_name, target_q_index in answer_refs:
|
71
|
+
# Find the source question index by name
|
72
|
+
source_q_index = next(
|
73
|
+
i
|
74
|
+
for i, q in enumerate(self.survey.questions)
|
75
|
+
if q.question_name == source_q_name
|
76
|
+
)
|
77
|
+
ref_edge = pydot.Edge(
|
78
|
+
f"Q{source_q_index}",
|
79
|
+
f"Q{target_q_index}",
|
80
|
+
style="dashed",
|
81
|
+
color="purple",
|
82
|
+
label="answer reference",
|
83
|
+
)
|
84
|
+
graph.add_edge(ref_edge)
|
85
|
+
|
86
|
+
# Add an "EndOfSurvey" node
|
87
|
+
graph.add_node(
|
88
|
+
pydot.Node("EndOfSurvey", label="End of Survey", shape="rectangle")
|
89
|
+
)
|
90
|
+
|
91
|
+
# Add edges for normal flow through the survey
|
92
|
+
num_questions = len(self.survey.questions)
|
93
|
+
for index in range(num_questions - 1):
|
94
|
+
graph.add_edge(pydot.Edge(f"Q{index}", f"Q{index+1}"))
|
95
|
+
|
96
|
+
graph.add_edge(pydot.Edge(f"Q{num_questions-1}", "EndOfSurvey"))
|
97
|
+
|
98
|
+
relevant_rules = [
|
99
|
+
rule
|
100
|
+
for rule in self.survey.rule_collection
|
101
|
+
if rule.priority > RulePriority.DEFAULT.value
|
102
|
+
]
|
103
|
+
|
104
|
+
# edge-colors to cycle through
|
105
|
+
colors = [
|
106
|
+
"blue",
|
107
|
+
"red",
|
108
|
+
"orange",
|
109
|
+
"purple",
|
110
|
+
"brown",
|
111
|
+
"cyan",
|
112
|
+
"green",
|
113
|
+
]
|
114
|
+
rule_colors = {
|
115
|
+
rule: colors[i % len(colors)] for i, rule in enumerate(relevant_rules)
|
116
|
+
}
|
117
|
+
|
118
|
+
for rule in relevant_rules:
|
119
|
+
color = rule_colors[rule]
|
120
|
+
edge_label = f"if {rule.expression}"
|
121
|
+
source_node = f"Q{rule.current_q}"
|
122
|
+
target_node = (
|
123
|
+
f"Q{rule.next_q}"
|
124
|
+
if rule.next_q != EndOfSurvey and rule.next_q < num_questions
|
125
|
+
else "EndOfSurvey"
|
126
|
+
)
|
127
|
+
if rule.before_rule:
|
128
|
+
edge = pydot.Edge(
|
129
|
+
source_node,
|
130
|
+
target_node,
|
131
|
+
label=edge_label,
|
132
|
+
color=color,
|
133
|
+
fontcolor=color,
|
134
|
+
tailport="n",
|
135
|
+
headport="n",
|
136
|
+
)
|
137
|
+
else:
|
138
|
+
edge = pydot.Edge(
|
139
|
+
source_node,
|
140
|
+
target_node,
|
141
|
+
label=edge_label,
|
142
|
+
color=color,
|
143
|
+
fontcolor=color,
|
144
|
+
)
|
145
|
+
|
146
|
+
graph.add_edge(edge)
|
147
|
+
|
148
|
+
if filename is not None:
|
149
|
+
graph.write_png(filename)
|
150
|
+
print(f"Flowchart saved to {filename}")
|
151
|
+
return
|
152
|
+
|
153
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp_file:
|
154
|
+
try:
|
155
|
+
graph.write_png(tmp_file.name)
|
156
|
+
except FileNotFoundError:
|
157
|
+
print(
|
158
|
+
"""File not found. Most likely it's because you don't have graphviz installed. Please install it and try again.
|
159
|
+
It's
|
160
|
+
$ sudo apt-get install graphviz
|
161
|
+
on Ubuntu.
|
162
|
+
"""
|
163
|
+
)
|
164
|
+
from edsl.utilities.is_notebook import is_notebook
|
165
|
+
|
166
|
+
if is_notebook():
|
167
|
+
from IPython.display import Image, display
|
168
|
+
|
169
|
+
display(Image(tmp_file.name))
|
170
|
+
else:
|
171
|
+
import os
|
172
|
+
import sys
|
173
|
+
|
174
|
+
if os.name == "nt": # Windows
|
175
|
+
os.system(f"start {tmp_file.name}")
|
176
|
+
elif os.name == "posix": # macOS, Linux, Unix, etc.
|
177
|
+
os.system(
|
178
|
+
f"open {tmp_file.name}"
|
179
|
+
if sys.platform == "darwin"
|
180
|
+
else f"xdg-open {tmp_file.name}"
|
181
|
+
)
|
@@ -0,0 +1,284 @@
|
|
1
|
+
import json
|
2
|
+
import html
|
3
|
+
import re
|
4
|
+
|
5
|
+
from edsl import Question
|
6
|
+
from edsl import Survey
|
7
|
+
|
8
|
+
qualtrics_codes = {
|
9
|
+
"TE": "free_text",
|
10
|
+
"MC": "multiple_choice",
|
11
|
+
"Matrix": "matrix",
|
12
|
+
"DB": "instruction", # not quite right, but for now
|
13
|
+
"Timing": "free_text", # not quite right, but for now
|
14
|
+
}
|
15
|
+
# TE (Text Entry): Allows respondents to input a text response.
|
16
|
+
# MC (Multiple Choice): Provides respondents with a list of options to choose from.
|
17
|
+
# DB (Descriptive Text or Information): Displays text or information without requiring a response.
|
18
|
+
# Matrix: A grid-style question where respondents can evaluate multiple items using the same set of response options.
|
19
|
+
|
20
|
+
|
21
|
+
def clean_html(raw_html):
|
22
|
+
# Unescape HTML entities
|
23
|
+
clean_text = html.unescape(raw_html)
|
24
|
+
# Remove HTML tags
|
25
|
+
clean_text = re.sub(r"<.*?>", "", clean_text)
|
26
|
+
# Replace non-breaking spaces with regular spaces
|
27
|
+
clean_text = clean_text.replace("\xa0", " ")
|
28
|
+
# Optionally, strip leading/trailing spaces
|
29
|
+
clean_text = clean_text.strip()
|
30
|
+
return clean_text
|
31
|
+
|
32
|
+
|
33
|
+
class QualtricsQuestion:
|
34
|
+
def __init__(self, question_json, debug=False):
|
35
|
+
self.debug = debug
|
36
|
+
self.question_json = question_json
|
37
|
+
if self.element != "SQ":
|
38
|
+
raise ValueError("Invalid question element type")
|
39
|
+
|
40
|
+
@property
|
41
|
+
def element(self):
|
42
|
+
return self.question_json["Element"]
|
43
|
+
|
44
|
+
@property
|
45
|
+
def selector(self):
|
46
|
+
return self.question_json.get("Selector", None)
|
47
|
+
|
48
|
+
@property
|
49
|
+
def question_name(self):
|
50
|
+
return self.question_json["PrimaryAttribute"]
|
51
|
+
|
52
|
+
@property
|
53
|
+
def question_text(self):
|
54
|
+
return clean_html(self.question_json["Payload"]["QuestionText"])
|
55
|
+
|
56
|
+
@property
|
57
|
+
def raw_question_type(self):
|
58
|
+
return self.question_json["Payload"]["QuestionType"]
|
59
|
+
|
60
|
+
@property
|
61
|
+
def question_type(self):
|
62
|
+
q_type = qualtrics_codes.get(self.raw_question_type, None)
|
63
|
+
if q_type is None:
|
64
|
+
print(f"Unknown question type: {self.raw_question_type}")
|
65
|
+
return None
|
66
|
+
return q_type
|
67
|
+
|
68
|
+
@property
|
69
|
+
def choices(self):
|
70
|
+
if "Choices" in self.question_json["Payload"]:
|
71
|
+
return [
|
72
|
+
choice["Display"]
|
73
|
+
for choice in self.question_json["Payload"]["Choices"].values()
|
74
|
+
]
|
75
|
+
return None
|
76
|
+
|
77
|
+
@property
|
78
|
+
def answers(self):
|
79
|
+
if "Answers" in self.question_json["Payload"]:
|
80
|
+
return [
|
81
|
+
choice["Display"]
|
82
|
+
for choice in self.question_json["Payload"]["Choices"].values()
|
83
|
+
]
|
84
|
+
return None
|
85
|
+
|
86
|
+
def to_edsl(self):
|
87
|
+
if self.question_type == "instruction":
|
88
|
+
from edsl import Instruction
|
89
|
+
|
90
|
+
return [Instruction(text=self.question_text, name=self.question_name)]
|
91
|
+
|
92
|
+
if self.question_type == "free_text":
|
93
|
+
try:
|
94
|
+
q = Question(
|
95
|
+
**{
|
96
|
+
"question_type": self.question_type,
|
97
|
+
"question_text": self.question_text,
|
98
|
+
"question_name": self.question_name,
|
99
|
+
}
|
100
|
+
)
|
101
|
+
return [q]
|
102
|
+
except Exception as e:
|
103
|
+
return []
|
104
|
+
|
105
|
+
if self.question_type == "multiple_choice":
|
106
|
+
# Let's figure of it it's actually a checkbox question
|
107
|
+
if self.selector == "MAVR" or self.selector == "MULTIPLE":
|
108
|
+
try:
|
109
|
+
q = Question(
|
110
|
+
**{
|
111
|
+
"question_type": "checkbox",
|
112
|
+
"question_text": self.question_text,
|
113
|
+
"question_name": self.question_name,
|
114
|
+
"question_options": self.choices,
|
115
|
+
}
|
116
|
+
)
|
117
|
+
return [q]
|
118
|
+
except Exception as e:
|
119
|
+
return []
|
120
|
+
|
121
|
+
# maybe it's a linear scale!
|
122
|
+
if "<br>" in self.choices[0]:
|
123
|
+
option_labels = {}
|
124
|
+
question_options = []
|
125
|
+
for choice in self.choices:
|
126
|
+
if "<br>" in choice:
|
127
|
+
option_label, question_option = choice.split("<br>")
|
128
|
+
option_labels[int(question_option)] = option_label
|
129
|
+
question_options.append(int(question_option))
|
130
|
+
else:
|
131
|
+
question_options.append(int(choice))
|
132
|
+
try:
|
133
|
+
q = Question(
|
134
|
+
**{
|
135
|
+
"question_type": "linear_scale",
|
136
|
+
"question_text": self.question_text,
|
137
|
+
"question_name": self.question_name,
|
138
|
+
"question_options": question_options,
|
139
|
+
"option_labels": option_labels,
|
140
|
+
}
|
141
|
+
)
|
142
|
+
return [q]
|
143
|
+
except Exception as e:
|
144
|
+
if self.debug:
|
145
|
+
raise e
|
146
|
+
else:
|
147
|
+
print(e)
|
148
|
+
return []
|
149
|
+
|
150
|
+
try:
|
151
|
+
q = Question(
|
152
|
+
**{
|
153
|
+
"question_type": self.question_type,
|
154
|
+
"question_text": self.question_text,
|
155
|
+
"question_name": self.question_name,
|
156
|
+
"question_options": self.choices,
|
157
|
+
}
|
158
|
+
)
|
159
|
+
return [q]
|
160
|
+
except Exception as e:
|
161
|
+
return []
|
162
|
+
|
163
|
+
if self.question_type == "matrix":
|
164
|
+
questions = []
|
165
|
+
for index, choice in enumerate(self.choices):
|
166
|
+
try:
|
167
|
+
q = Question(
|
168
|
+
**{
|
169
|
+
"question_type": "multiple_choice",
|
170
|
+
"question_text": self.question_text + f" ({choice})",
|
171
|
+
"question_name": self.question_name + f"_{index}",
|
172
|
+
"question_options": self.answers,
|
173
|
+
}
|
174
|
+
)
|
175
|
+
questions.append(q)
|
176
|
+
except Exception as e:
|
177
|
+
continue
|
178
|
+
|
179
|
+
return questions
|
180
|
+
|
181
|
+
raise ValueError(f"Invalid question type: {self.question_type}")
|
182
|
+
|
183
|
+
|
184
|
+
class SurveyQualtricsImport:
|
185
|
+
def __init__(self, qsf_file_name: str):
|
186
|
+
self.qsf_file_name = qsf_file_name
|
187
|
+
self.question_data = self.extract_questions_from_json()
|
188
|
+
|
189
|
+
def create_survey(self):
|
190
|
+
questions = []
|
191
|
+
for qualtrics_questions in self.question_data:
|
192
|
+
questions.extend(qualtrics_questions.to_edsl())
|
193
|
+
return Survey(questions)
|
194
|
+
|
195
|
+
@property
|
196
|
+
def survey_data(self):
|
197
|
+
with open(self.qsf_file_name, "r") as f:
|
198
|
+
survey_data = json.load(f)
|
199
|
+
return survey_data
|
200
|
+
|
201
|
+
def extract_questions_from_json(self):
|
202
|
+
questions = self.survey_data["SurveyElements"]
|
203
|
+
|
204
|
+
extracted_questions = []
|
205
|
+
|
206
|
+
for question in questions:
|
207
|
+
if question["Element"] == "SQ":
|
208
|
+
extracted_questions.append(QualtricsQuestion(question))
|
209
|
+
|
210
|
+
return extracted_questions
|
211
|
+
|
212
|
+
def extract_blocks_from_json(self):
|
213
|
+
blocks = []
|
214
|
+
|
215
|
+
for element in self.survey_data["SurveyElements"]:
|
216
|
+
if element["Element"] == "BL":
|
217
|
+
for block_payload in element["Payload"]:
|
218
|
+
block_elements = [
|
219
|
+
BlockElement(be["Type"], be["QuestionID"])
|
220
|
+
for be in block_payload["BlockElements"]
|
221
|
+
]
|
222
|
+
options_data = block_payload.get("Options", {})
|
223
|
+
options = BlockOptions(
|
224
|
+
options_data.get("BlockLocking", "false"),
|
225
|
+
options_data.get("RandomizeQuestions", "false"),
|
226
|
+
options_data.get("BlockVisibility", "Collapsed"),
|
227
|
+
)
|
228
|
+
|
229
|
+
block = SurveyBlock(
|
230
|
+
block_payload["Type"],
|
231
|
+
block_payload["Description"],
|
232
|
+
block_payload["ID"],
|
233
|
+
block_elements,
|
234
|
+
options,
|
235
|
+
)
|
236
|
+
blocks.append(block)
|
237
|
+
|
238
|
+
return blocks
|
239
|
+
|
240
|
+
|
241
|
+
class SurveyBlock:
|
242
|
+
def __init__(self, block_type, description, block_id, block_elements, options):
|
243
|
+
self.block_type = block_type
|
244
|
+
self.description = description
|
245
|
+
self.block_id = block_id
|
246
|
+
self.block_elements = block_elements
|
247
|
+
self.options = options
|
248
|
+
|
249
|
+
def __repr__(self):
|
250
|
+
return f"SurveyBlock(type={self.block_type}, description={self.description}, id={self.block_id})"
|
251
|
+
|
252
|
+
|
253
|
+
class BlockElement:
|
254
|
+
def __init__(self, element_type, question_id):
|
255
|
+
self.element_type = element_type
|
256
|
+
self.question_id = question_id
|
257
|
+
|
258
|
+
def __repr__(self):
|
259
|
+
return f"BlockElement(type={self.element_type}, question_id={self.question_id})"
|
260
|
+
|
261
|
+
|
262
|
+
class BlockOptions:
|
263
|
+
def __init__(self, block_locking, randomize_questions, block_visibility):
|
264
|
+
self.block_locking = block_locking
|
265
|
+
self.randomize_questions = randomize_questions
|
266
|
+
self.block_visibility = block_visibility
|
267
|
+
|
268
|
+
def __repr__(self):
|
269
|
+
return (
|
270
|
+
f"BlockOptions(block_locking={self.block_locking}, "
|
271
|
+
f"randomize_questions={self.randomize_questions}, "
|
272
|
+
f"block_visibility={self.block_visibility})"
|
273
|
+
)
|
274
|
+
|
275
|
+
|
276
|
+
if __name__ == "__main__":
|
277
|
+
survey_creator = SurveyQualtricsImport("example.qsf")
|
278
|
+
# print(survey_creator.question_data)
|
279
|
+
# survey = survey_creator.create_survey()
|
280
|
+
# info = survey.push()
|
281
|
+
# print(info)
|
282
|
+
# questions = survey.extract_questions_from_json()
|
283
|
+
# for question in questions:
|
284
|
+
# print(question)
|
@@ -0,0 +1,141 @@
|
|
1
|
+
from fastapi import FastAPI
|
2
|
+
from pydantic import BaseModel, create_model
|
3
|
+
from typing import Callable, Optional, Type, Dict, Any, List, Union
|
4
|
+
|
5
|
+
|
6
|
+
class SurveyToApp:
|
7
|
+
def __init__(self, survey):
|
8
|
+
self.survey = survey
|
9
|
+
self.app = FastAPI()
|
10
|
+
|
11
|
+
def parameters(self):
|
12
|
+
return self.survey.parameters
|
13
|
+
|
14
|
+
def create_input(self) -> Type[BaseModel]:
|
15
|
+
"""
|
16
|
+
Creates a Pydantic model based on the survey parameters.
|
17
|
+
Returns:
|
18
|
+
Type[BaseModel]: A dynamically created Pydantic model class
|
19
|
+
"""
|
20
|
+
# Get parameters from survey - now calling the method
|
21
|
+
params = self.parameters()
|
22
|
+
|
23
|
+
# Create field definitions dictionary
|
24
|
+
fields: Dict[str, Any] = {}
|
25
|
+
|
26
|
+
# Since params is a set, we'll handle each parameter directly
|
27
|
+
# Assuming each parameter in the set has the necessary attributes
|
28
|
+
for param in params:
|
29
|
+
# You might need to adjust these based on the actual parameter object structure
|
30
|
+
param_name = getattr(param, "name", str(param))
|
31
|
+
param_type = getattr(param, "type", "string")
|
32
|
+
is_required = getattr(param, "required", True)
|
33
|
+
|
34
|
+
# Map survey parameter types to Python types
|
35
|
+
type_mapping = {
|
36
|
+
"string": str,
|
37
|
+
"integer": int,
|
38
|
+
"float": float,
|
39
|
+
"boolean": bool,
|
40
|
+
"array": List,
|
41
|
+
# Add more type mappings as needed
|
42
|
+
}
|
43
|
+
|
44
|
+
# Get the Python type from mapping
|
45
|
+
python_type = type_mapping.get(param_type, str)
|
46
|
+
|
47
|
+
if is_required:
|
48
|
+
fields[param_name] = (python_type, ...)
|
49
|
+
else:
|
50
|
+
fields[param_name] = (Optional[python_type], None)
|
51
|
+
|
52
|
+
# Add the template variable 'name' that's used in the question text
|
53
|
+
fields["name"] = (str, ...)
|
54
|
+
|
55
|
+
# Create and return the Pydantic model
|
56
|
+
model_name = f"{self.survey.__class__.__name__}Model"
|
57
|
+
return create_model(model_name, **fields)
|
58
|
+
|
59
|
+
def create_route(self) -> Callable:
|
60
|
+
"""
|
61
|
+
Creates a FastAPI route handler for the survey.
|
62
|
+
Returns:
|
63
|
+
Callable: A route handler function
|
64
|
+
"""
|
65
|
+
input_model = self.create_input()
|
66
|
+
|
67
|
+
async def route_handler(input_data: input_model):
|
68
|
+
"""
|
69
|
+
Handles the API route by processing the input data through the survey.
|
70
|
+
Args:
|
71
|
+
input_data: The validated input data matching the created Pydantic model
|
72
|
+
Returns:
|
73
|
+
dict: The processed survey results
|
74
|
+
"""
|
75
|
+
# Convert Pydantic model to dict
|
76
|
+
data = input_data.dict()
|
77
|
+
print(data)
|
78
|
+
from edsl.scenarios.Scenario import Scenario
|
79
|
+
|
80
|
+
# Process the data through the survey
|
81
|
+
try:
|
82
|
+
s = Scenario(data)
|
83
|
+
results = self.survey.by(s).run()
|
84
|
+
return {
|
85
|
+
"status": "success",
|
86
|
+
"data": results.select("answer.*").to_scenario_list().to_dict(),
|
87
|
+
}
|
88
|
+
except Exception as e:
|
89
|
+
return {"status": "error", "message": str(e)}
|
90
|
+
|
91
|
+
return route_handler
|
92
|
+
|
93
|
+
def add_to_app(
|
94
|
+
self, app: FastAPI, path: str = "/survey", methods: List[str] = ["POST", "GET"]
|
95
|
+
):
|
96
|
+
"""
|
97
|
+
Adds the survey route to a FastAPI application.
|
98
|
+
Args:
|
99
|
+
app (FastAPI): The FastAPI application instance
|
100
|
+
path (str): The API endpoint path
|
101
|
+
methods (List[str]): HTTP methods to support
|
102
|
+
"""
|
103
|
+
route_handler = self.create_route()
|
104
|
+
input_model = self.create_input()
|
105
|
+
|
106
|
+
app.add_api_route(
|
107
|
+
path, route_handler, methods=methods, response_model=Dict[str, Any]
|
108
|
+
)
|
109
|
+
|
110
|
+
def create_app(self, path: str = "/survey", methods: List[str] = ["POST", "GET"]):
|
111
|
+
"""
|
112
|
+
Creates a FastAPI application with the survey route.
|
113
|
+
Args:
|
114
|
+
path (str): The API endpoint path
|
115
|
+
methods (List[str]): HTTP methods to support
|
116
|
+
Returns:
|
117
|
+
FastAPI: The FastAPI application instance
|
118
|
+
"""
|
119
|
+
app = FastAPI()
|
120
|
+
self.add_to_app(app, path=path, methods=methods)
|
121
|
+
return app
|
122
|
+
|
123
|
+
|
124
|
+
from edsl import QuestionFreeText, QuestionList
|
125
|
+
|
126
|
+
# q = QuestionFreeText(
|
127
|
+
# question_name="name_gender",
|
128
|
+
# question_text="Is this customarily a boy's name or a girl's name: {{ name}}",
|
129
|
+
# )
|
130
|
+
|
131
|
+
q = QuestionList(
|
132
|
+
question_name="examples",
|
133
|
+
question_text="Give me {{ num }} examples of {{ thing }}",
|
134
|
+
)
|
135
|
+
|
136
|
+
survey_app = SurveyToApp(q.to_survey())
|
137
|
+
|
138
|
+
if __name__ == "__main__":
|
139
|
+
import uvicorn
|
140
|
+
|
141
|
+
uvicorn.run(survey_app.create_app(path="/examples"), host="127.0.0.1", port=8000)
|
edsl/surveys/__init__.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
1
|
from edsl.surveys.Survey import Survey
|
2
|
-
from edsl.surveys.
|
3
|
-
|
2
|
+
from edsl.surveys.instructions.Instruction import Instruction
|
3
|
+
|
4
|
+
# from edsl.surveys.Rule import Rule
|
5
|
+
# from edsl.surveys.RuleCollection import RuleCollection
|
edsl/surveys/base.py
CHANGED
@@ -1,33 +1,49 @@
|
|
1
|
+
"""Base classes and enumerations for the surveyor package."""
|
2
|
+
|
1
3
|
from enum import Enum
|
2
4
|
|
3
5
|
|
4
6
|
class RulePriority(Enum):
|
7
|
+
"""Enumeration of the priority of a rule."""
|
8
|
+
|
5
9
|
DEFAULT = -1
|
6
10
|
|
7
11
|
|
8
12
|
class EndOfSurveyParent:
|
9
|
-
"A named object that represents the end of the survey."
|
13
|
+
"""A named object that represents the end of the survey."""
|
14
|
+
|
10
15
|
pass
|
11
16
|
|
12
17
|
def __repr__(self):
|
18
|
+
"""Return a string representation of the object."""
|
13
19
|
return "EndOfSurvey"
|
14
20
|
|
15
21
|
def __str__(self):
|
22
|
+
"""Return a string representation of the object."""
|
16
23
|
return "EndOfSurvey"
|
17
24
|
|
18
25
|
def __bool__(self):
|
26
|
+
"""Return False."""
|
19
27
|
return False
|
20
28
|
|
21
29
|
def __add__(self, other):
|
22
|
-
"""
|
30
|
+
"""Add the object to another object.
|
31
|
+
|
32
|
+
Example:
|
23
33
|
>>> e = EndOfSurveyParent()
|
24
34
|
>>> e + 1
|
25
35
|
EndOfSurvey
|
26
36
|
"""
|
27
37
|
return self
|
28
38
|
|
39
|
+
def __deepcopy__(self, memo):
|
40
|
+
# Return the same instance when deepcopy is called
|
41
|
+
return self
|
42
|
+
|
29
43
|
def __radd__(self, other):
|
30
|
-
"""
|
44
|
+
"""Add the object to another object.
|
45
|
+
|
46
|
+
Example:
|
31
47
|
>>> 1 + EndOfSurveyParent()
|
32
48
|
EndOfSurvey
|
33
49
|
"""
|