edsl 0.1.29.dev3__py3-none-any.whl → 0.1.30__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 +18 -18
- edsl/__init__.py +23 -23
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +79 -41
- edsl/agents/AgentList.py +26 -26
- edsl/agents/Invigilator.py +19 -2
- edsl/agents/InvigilatorBase.py +15 -10
- edsl/agents/PromptConstructionMixin.py +342 -100
- edsl/agents/descriptors.py +2 -1
- edsl/base/Base.py +289 -0
- edsl/config.py +2 -1
- edsl/conjure/InputData.py +39 -8
- edsl/conversation/car_buying.py +1 -1
- edsl/coop/coop.py +187 -150
- edsl/coop/utils.py +43 -75
- edsl/data/Cache.py +41 -18
- edsl/data/CacheEntry.py +6 -7
- edsl/data/SQLiteDict.py +11 -3
- edsl/data_transfer_models.py +4 -0
- edsl/jobs/Answers.py +15 -1
- edsl/jobs/Jobs.py +108 -49
- edsl/jobs/buckets/ModelBuckets.py +14 -2
- edsl/jobs/buckets/TokenBucket.py +32 -5
- edsl/jobs/interviews/Interview.py +99 -79
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +19 -24
- edsl/jobs/runners/JobsRunnerAsyncio.py +16 -16
- edsl/jobs/tasks/QuestionTaskCreator.py +10 -6
- edsl/jobs/tasks/TaskHistory.py +4 -3
- edsl/language_models/LanguageModel.py +17 -17
- edsl/language_models/ModelList.py +1 -1
- edsl/language_models/repair.py +8 -7
- edsl/notebooks/Notebook.py +47 -10
- edsl/prompts/Prompt.py +31 -19
- edsl/questions/QuestionBase.py +38 -13
- edsl/questions/QuestionBudget.py +5 -6
- edsl/questions/QuestionCheckBox.py +7 -3
- edsl/questions/QuestionExtract.py +5 -3
- edsl/questions/QuestionFreeText.py +7 -5
- edsl/questions/QuestionFunctional.py +34 -5
- edsl/questions/QuestionList.py +3 -4
- edsl/questions/QuestionMultipleChoice.py +68 -12
- edsl/questions/QuestionNumerical.py +4 -3
- edsl/questions/QuestionRank.py +5 -3
- edsl/questions/__init__.py +4 -3
- edsl/questions/descriptors.py +46 -4
- edsl/questions/question_registry.py +20 -31
- edsl/questions/settings.py +1 -1
- edsl/results/Dataset.py +31 -0
- edsl/results/DatasetExportMixin.py +570 -0
- edsl/results/Result.py +66 -70
- edsl/results/Results.py +160 -68
- edsl/results/ResultsDBMixin.py +7 -3
- edsl/results/ResultsExportMixin.py +22 -537
- edsl/results/ResultsGGMixin.py +3 -3
- edsl/results/ResultsToolsMixin.py +5 -5
- edsl/scenarios/FileStore.py +299 -0
- edsl/scenarios/Scenario.py +16 -24
- edsl/scenarios/ScenarioList.py +42 -17
- edsl/scenarios/ScenarioListExportMixin.py +32 -0
- edsl/scenarios/ScenarioListPdfMixin.py +2 -1
- edsl/scenarios/__init__.py +1 -0
- edsl/study/Study.py +8 -16
- edsl/surveys/MemoryPlan.py +11 -4
- edsl/surveys/Survey.py +88 -17
- edsl/surveys/SurveyExportMixin.py +4 -2
- edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
- edsl/tools/plotting.py +4 -2
- edsl/utilities/__init__.py +21 -21
- edsl/utilities/interface.py +66 -45
- edsl/utilities/utilities.py +11 -13
- {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/METADATA +11 -10
- {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/RECORD +74 -71
- {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/WHEEL +1 -1
- edsl-0.1.29.dev3.dist-info/entry_points.txt +0 -3
- {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/LICENSE +0 -0
@@ -1,16 +1,37 @@
|
|
1
1
|
from typing import Optional, Callable
|
2
|
-
from edsl.questions.QuestionBase import QuestionBase
|
3
|
-
from edsl.questions.descriptors import FunctionDescriptor
|
4
2
|
import inspect
|
5
3
|
|
6
4
|
from edsl.questions.QuestionBase import QuestionBase
|
7
|
-
from edsl.questions.descriptors import FunctionDescriptor
|
8
5
|
|
9
6
|
from edsl.utilities.restricted_python import create_restricted_function
|
7
|
+
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
10
8
|
|
11
9
|
|
12
10
|
class QuestionFunctional(QuestionBase):
|
13
|
-
"""A special type of question that is *not* answered by an LLM.
|
11
|
+
"""A special type of question that is *not* answered by an LLM.
|
12
|
+
|
13
|
+
>>> from edsl import Scenario, Agent
|
14
|
+
|
15
|
+
# Create an instance of QuestionFunctional with the new function
|
16
|
+
>>> question = QuestionFunctional.example()
|
17
|
+
|
18
|
+
# Activate and test the function
|
19
|
+
>>> question.activate()
|
20
|
+
>>> scenario = Scenario({"numbers": [1, 2, 3, 4, 5]})
|
21
|
+
>>> agent = Agent(traits={"multiplier": 10})
|
22
|
+
>>> results = question.by(scenario).by(agent).run()
|
23
|
+
>>> results.select("answer.*").to_list()[0] == 150
|
24
|
+
True
|
25
|
+
|
26
|
+
# Serialize the question to a dictionary
|
27
|
+
|
28
|
+
>>> from edsl.questions.QuestionBase import QuestionBase
|
29
|
+
>>> new_question = QuestionBase.from_dict(question.to_dict())
|
30
|
+
>>> results = new_question.by(scenario).by(agent).run()
|
31
|
+
>>> results.select("answer.*").to_list()[0] == 150
|
32
|
+
True
|
33
|
+
|
34
|
+
"""
|
14
35
|
|
15
36
|
question_type = "functional"
|
16
37
|
default_instructions = ""
|
@@ -76,6 +97,7 @@ class QuestionFunctional(QuestionBase):
|
|
76
97
|
"""Required by Question, but not used by QuestionFunctional."""
|
77
98
|
raise NotImplementedError
|
78
99
|
|
100
|
+
@add_edsl_version
|
79
101
|
def to_dict(self):
|
80
102
|
return {
|
81
103
|
"question_name": self.question_name,
|
@@ -116,4 +138,11 @@ def main():
|
|
116
138
|
scenario = Scenario({"numbers": [1, 2, 3, 4, 5]})
|
117
139
|
agent = Agent(traits={"multiplier": 10})
|
118
140
|
results = question.by(scenario).by(agent).run()
|
119
|
-
|
141
|
+
assert results.select("answer.*").to_list()[0] == 150
|
142
|
+
|
143
|
+
|
144
|
+
if __name__ == "__main__":
|
145
|
+
# main()
|
146
|
+
import doctest
|
147
|
+
|
148
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
edsl/questions/QuestionList.py
CHANGED
@@ -5,9 +5,6 @@ from typing import Any, Optional, Union
|
|
5
5
|
from edsl.questions.QuestionBase import QuestionBase
|
6
6
|
from edsl.questions.descriptors import IntegerOrNoneDescriptor
|
7
7
|
|
8
|
-
from edsl.scenarios import Scenario
|
9
|
-
from edsl.utilities import random_string
|
10
|
-
|
11
8
|
|
12
9
|
class QuestionList(QuestionBase):
|
13
10
|
"""This question prompts the agent to answer by providing a list of items as comma-separated strings."""
|
@@ -42,13 +39,15 @@ class QuestionList(QuestionBase):
|
|
42
39
|
self._validate_answer_list(answer)
|
43
40
|
return answer
|
44
41
|
|
45
|
-
def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
|
42
|
+
def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
|
46
43
|
"""There is no answer code."""
|
47
44
|
return answer
|
48
45
|
|
49
46
|
def _simulate_answer(self, human_readable: bool = True):
|
50
47
|
"""Simulate a valid answer for debugging purposes (what the validator expects)."""
|
51
48
|
num_items = random.randint(1, self.max_list_items or 2)
|
49
|
+
from edsl.utilities.utilities import random_string
|
50
|
+
|
52
51
|
return {"answer": [random_string() for _ in range(num_items)]}
|
53
52
|
|
54
53
|
@property
|
@@ -1,17 +1,20 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
|
2
|
+
import time
|
3
|
+
from typing import Union
|
3
4
|
import random
|
4
|
-
|
5
|
+
from typing import Optional
|
5
6
|
from jinja2 import Template
|
6
7
|
|
7
|
-
from edsl.utilities import random_string
|
8
|
-
from edsl.questions.descriptors import QuestionOptionsDescriptor
|
9
8
|
from edsl.questions.QuestionBase import QuestionBase
|
10
|
-
from edsl.
|
9
|
+
from edsl.questions.descriptors import QuestionOptionsDescriptor
|
11
10
|
|
12
11
|
|
13
12
|
class QuestionMultipleChoice(QuestionBase):
|
14
|
-
"""This question prompts the agent to select one option from a list of options.
|
13
|
+
"""This question prompts the agent to select one option from a list of options.
|
14
|
+
|
15
|
+
https://docs.expectedparrot.com/en/latest/questions.html#questionmultiplechoice-class
|
16
|
+
|
17
|
+
"""
|
15
18
|
|
16
19
|
question_type = "multiple_choice"
|
17
20
|
purpose = "When options are known and limited"
|
@@ -36,29 +39,79 @@ class QuestionMultipleChoice(QuestionBase):
|
|
36
39
|
self.question_text = question_text
|
37
40
|
self.question_options = question_options
|
38
41
|
|
42
|
+
# @property
|
43
|
+
# def question_options(self) -> Union[list[str], list[list], list[float], list[int]]:
|
44
|
+
# """Return the question options."""
|
45
|
+
# return self._question_options
|
46
|
+
|
39
47
|
################
|
40
48
|
# Answer methods
|
41
49
|
################
|
42
50
|
def _validate_answer(
|
43
51
|
self, answer: dict[str, Union[str, int]]
|
44
52
|
) -> dict[str, Union[str, int]]:
|
45
|
-
"""Validate the answer.
|
53
|
+
"""Validate the answer.
|
54
|
+
|
55
|
+
>>> q = QuestionMultipleChoice.example()
|
56
|
+
>>> q._validate_answer({"answer": 0, "comment": "I like custard"})
|
57
|
+
{'answer': 0, 'comment': 'I like custard'}
|
58
|
+
|
59
|
+
>>> q = QuestionMultipleChoice(question_name="how_feeling", question_text="How are you?", question_options=["Good", "Great", "OK", "Bad"])
|
60
|
+
>>> q._validate_answer({"answer": -1, "comment": "I like custard"})
|
61
|
+
Traceback (most recent call last):
|
62
|
+
...
|
63
|
+
edsl.exceptions.questions.QuestionAnswerValidationError: Answer code must be a non-negative integer (got -1).
|
64
|
+
"""
|
46
65
|
self._validate_answer_template_basic(answer)
|
47
66
|
self._validate_answer_multiple_choice(answer)
|
48
67
|
return answer
|
49
68
|
|
50
|
-
def _translate_answer_code_to_answer(
|
51
|
-
|
69
|
+
def _translate_answer_code_to_answer(
|
70
|
+
self, answer_code: int, scenario: Optional["Scenario"] = None
|
71
|
+
):
|
72
|
+
"""Translate the answer code to the actual answer.
|
73
|
+
|
74
|
+
It is used to translate the answer code to the actual answer.
|
75
|
+
The question options might be templates, so they need to be rendered with the scenario.
|
76
|
+
|
77
|
+
>>> q = QuestionMultipleChoice.example()
|
78
|
+
>>> q._translate_answer_code_to_answer(0, {})
|
79
|
+
'Good'
|
80
|
+
|
81
|
+
>>> q = QuestionMultipleChoice(question_name="how_feeling", question_text="How are you?", question_options=["{{emotion[0]}}", "emotion[1]"])
|
82
|
+
>>> q._translate_answer_code_to_answer(0, {"emotion": ["Happy", "Sad"]})
|
83
|
+
'Happy'
|
84
|
+
|
85
|
+
"""
|
86
|
+
from edsl.scenarios.Scenario import Scenario
|
87
|
+
|
52
88
|
scenario = scenario or Scenario()
|
53
|
-
|
54
|
-
|
55
|
-
|
89
|
+
|
90
|
+
if isinstance(self.question_options, str):
|
91
|
+
# If dynamic options are provided like {{ options }}, render them with the scenario
|
92
|
+
from jinja2 import Environment, meta
|
93
|
+
|
94
|
+
env = Environment()
|
95
|
+
parsed_content = env.parse(self.question_options)
|
96
|
+
question_option_key = list(meta.find_undeclared_variables(parsed_content))[
|
97
|
+
0
|
98
|
+
]
|
99
|
+
translated_options = scenario.get(question_option_key)
|
100
|
+
else:
|
101
|
+
translated_options = [
|
102
|
+
Template(str(option)).render(scenario)
|
103
|
+
for option in self.question_options
|
104
|
+
]
|
105
|
+
# print("Translated options:", translated_options)
|
106
|
+
# breakpoint()
|
56
107
|
return translated_options[int(answer_code)]
|
57
108
|
|
58
109
|
def _simulate_answer(
|
59
110
|
self, human_readable: bool = True
|
60
111
|
) -> dict[str, Union[int, str]]:
|
61
112
|
"""Simulate a valid answer for debugging purposes."""
|
113
|
+
from edsl.utilities.utilities import random_string
|
114
|
+
|
62
115
|
if human_readable:
|
63
116
|
answer = random.choice(self.question_options)
|
64
117
|
else:
|
@@ -70,6 +123,7 @@ class QuestionMultipleChoice(QuestionBase):
|
|
70
123
|
|
71
124
|
@property
|
72
125
|
def question_html_content(self) -> str:
|
126
|
+
"""Return the HTML version of the question."""
|
73
127
|
if hasattr(self, "option_labels"):
|
74
128
|
option_labels = self.option_labels
|
75
129
|
else:
|
@@ -127,6 +181,8 @@ def main():
|
|
127
181
|
q.to_dict()
|
128
182
|
assert q.from_dict(q.to_dict()) == q
|
129
183
|
|
184
|
+
|
185
|
+
if __name__ == "__main__":
|
130
186
|
import doctest
|
131
187
|
|
132
188
|
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
@@ -2,11 +2,10 @@ from __future__ import annotations
|
|
2
2
|
import textwrap
|
3
3
|
from random import uniform
|
4
4
|
from typing import Any, Optional, Union
|
5
|
+
|
5
6
|
from edsl.exceptions import QuestionAnswerValidationError
|
6
7
|
from edsl.questions.QuestionBase import QuestionBase
|
7
8
|
from edsl.questions.descriptors import NumericalOrNoneDescriptor
|
8
|
-
from edsl.scenarios import Scenario
|
9
|
-
from edsl.utilities import random_string
|
10
9
|
|
11
10
|
|
12
11
|
class QuestionNumerical(QuestionBase):
|
@@ -48,12 +47,14 @@ class QuestionNumerical(QuestionBase):
|
|
48
47
|
self._validate_answer_numerical(answer)
|
49
48
|
return answer
|
50
49
|
|
51
|
-
def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
|
50
|
+
def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
|
52
51
|
"""There is no answer code."""
|
53
52
|
return answer
|
54
53
|
|
55
54
|
def _simulate_answer(self, human_readable: bool = True):
|
56
55
|
"""Simulate a valid answer for debugging purposes."""
|
56
|
+
from edsl.utilities.utilities import random_string
|
57
|
+
|
57
58
|
return {
|
58
59
|
"answer": uniform(self.min_value, self.max_value),
|
59
60
|
"comment": random_string(),
|
edsl/questions/QuestionRank.py
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import random
|
3
|
-
import textwrap
|
4
3
|
from jinja2 import Template
|
5
4
|
from typing import Any, Optional, Union
|
6
5
|
from edsl.questions.QuestionBase import QuestionBase
|
7
6
|
from edsl.exceptions import QuestionAnswerValidationError
|
8
|
-
|
9
|
-
from edsl.utilities.utilities import random_string
|
7
|
+
|
10
8
|
from edsl.questions.descriptors import (
|
11
9
|
QuestionOptionsDescriptor,
|
12
10
|
NumSelectionsDescriptor,
|
@@ -55,6 +53,8 @@ class QuestionRank(QuestionBase):
|
|
55
53
|
self, answer_codes, scenario: Scenario = None
|
56
54
|
) -> list[str]:
|
57
55
|
"""Translate the answer code to the actual answer."""
|
56
|
+
from edsl.scenarios import Scenario
|
57
|
+
|
58
58
|
scenario = scenario or Scenario()
|
59
59
|
translated_options = [
|
60
60
|
Template(option).render(scenario) for option in self.question_options
|
@@ -66,6 +66,8 @@ class QuestionRank(QuestionBase):
|
|
66
66
|
|
67
67
|
def _simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
|
68
68
|
"""Simulate a valid answer for debugging purposes."""
|
69
|
+
from edsl.utilities.utilities import random_string
|
70
|
+
|
69
71
|
if human_readable:
|
70
72
|
selected = random.sample(self.question_options, self.num_selections)
|
71
73
|
else:
|
edsl/questions/__init__.py
CHANGED
@@ -10,17 +10,18 @@ from edsl.questions.QuestionBudget import QuestionBudget
|
|
10
10
|
from edsl.questions.QuestionCheckBox import QuestionCheckBox
|
11
11
|
from edsl.questions.QuestionExtract import QuestionExtract
|
12
12
|
from edsl.questions.QuestionFreeText import QuestionFreeText
|
13
|
+
|
13
14
|
from edsl.questions.QuestionFunctional import QuestionFunctional
|
14
15
|
from edsl.questions.QuestionList import QuestionList
|
15
16
|
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
16
17
|
from edsl.questions.QuestionNumerical import QuestionNumerical
|
17
18
|
from edsl.questions.QuestionRank import QuestionRank
|
18
19
|
|
19
|
-
# Questions derived from core questions
|
20
|
+
# # Questions derived from core questions
|
20
21
|
from edsl.questions.derived.QuestionLikertFive import QuestionLikertFive
|
21
22
|
from edsl.questions.derived.QuestionLinearScale import QuestionLinearScale
|
22
23
|
from edsl.questions.derived.QuestionTopK import QuestionTopK
|
23
24
|
from edsl.questions.derived.QuestionYesNo import QuestionYesNo
|
24
25
|
|
25
|
-
# Compose Questions
|
26
|
-
from edsl.questions.compose_questions import compose_questions
|
26
|
+
# # Compose Questions
|
27
|
+
# from edsl.questions.compose_questions import compose_questions
|
edsl/questions/descriptors.py
CHANGED
@@ -2,15 +2,13 @@
|
|
2
2
|
|
3
3
|
from abc import ABC, abstractmethod
|
4
4
|
import re
|
5
|
-
from typing import Any, Callable
|
5
|
+
from typing import Any, Callable, List, Optional
|
6
6
|
from edsl.exceptions import (
|
7
7
|
QuestionCreationValidationError,
|
8
8
|
QuestionAnswerValidationError,
|
9
9
|
)
|
10
10
|
from edsl.questions.settings import Settings
|
11
|
-
from edsl.utilities.utilities import is_valid_variable_name
|
12
11
|
|
13
|
-
from edsl.prompts import get_classes
|
14
12
|
|
15
13
|
################################
|
16
14
|
# Helper functions
|
@@ -56,6 +54,8 @@ class BaseDescriptor(ABC):
|
|
56
54
|
def __set__(self, instance, value: Any) -> None:
|
57
55
|
"""Set the value of the attribute."""
|
58
56
|
self.validate(value, instance)
|
57
|
+
from edsl.prompts.registry import get_classes
|
58
|
+
|
59
59
|
instance.__dict__[self.name] = value
|
60
60
|
if self.name == "_instructions":
|
61
61
|
instructions = value
|
@@ -231,6 +231,8 @@ class QuestionNameDescriptor(BaseDescriptor):
|
|
231
231
|
|
232
232
|
def validate(self, value, instance):
|
233
233
|
"""Validate the value is a valid variable name."""
|
234
|
+
from edsl.utilities.utilities import is_valid_variable_name
|
235
|
+
|
234
236
|
if not is_valid_variable_name(value):
|
235
237
|
raise QuestionCreationValidationError(
|
236
238
|
f"`question_name` is not a valid variable name (got {value})."
|
@@ -240,6 +242,16 @@ class QuestionNameDescriptor(BaseDescriptor):
|
|
240
242
|
class QuestionOptionsDescriptor(BaseDescriptor):
|
241
243
|
"""Validate that `question_options` is a list, does not exceed the min/max lengths, and has unique items."""
|
242
244
|
|
245
|
+
@classmethod
|
246
|
+
def example(cls):
|
247
|
+
class TestQuestion:
|
248
|
+
question_options = QuestionOptionsDescriptor()
|
249
|
+
|
250
|
+
def __init__(self, question_options: List[str]):
|
251
|
+
self.question_options = question_options
|
252
|
+
|
253
|
+
return TestQuestion
|
254
|
+
|
243
255
|
def __init__(
|
244
256
|
self,
|
245
257
|
num_choices: int = None,
|
@@ -252,7 +264,31 @@ class QuestionOptionsDescriptor(BaseDescriptor):
|
|
252
264
|
self.q_budget = q_budget
|
253
265
|
|
254
266
|
def validate(self, value: Any, instance) -> None:
|
255
|
-
"""Validate the question options.
|
267
|
+
"""Validate the question options.
|
268
|
+
|
269
|
+
>>> q_class = QuestionOptionsDescriptor.example()
|
270
|
+
>>> _ = q_class(["a", "b", "c"])
|
271
|
+
>>> _ = q_class(["a", "b", "c", "d", "d"])
|
272
|
+
Traceback (most recent call last):
|
273
|
+
...
|
274
|
+
edsl.exceptions.questions.QuestionCreationValidationError: Question options must be unique (got ['a', 'b', 'c', 'd', 'd']).
|
275
|
+
|
276
|
+
We allow dynamic question options, which are strings of the form '{{ question_options }}'.
|
277
|
+
|
278
|
+
>>> _ = q_class("{{dynamic_options}}")
|
279
|
+
>>> _ = q_class("dynamic_options")
|
280
|
+
Traceback (most recent call last):
|
281
|
+
...
|
282
|
+
edsl.exceptions.questions.QuestionCreationValidationError: Dynamic question options must be of the form: '{{ question_options }}'.
|
283
|
+
"""
|
284
|
+
if isinstance(value, str):
|
285
|
+
# Check if the string is a dynamic question option
|
286
|
+
if "{{" in value and "}}" in value:
|
287
|
+
return None
|
288
|
+
else:
|
289
|
+
raise QuestionCreationValidationError(
|
290
|
+
"Dynamic question options must be of the form: '{{ question_options }}'."
|
291
|
+
)
|
256
292
|
if not isinstance(value, list):
|
257
293
|
raise QuestionCreationValidationError(
|
258
294
|
f"Question options must be a list (got {value})."
|
@@ -337,3 +373,9 @@ class QuestionTextDescriptor(BaseDescriptor):
|
|
337
373
|
f"WARNING: Question text contains a single-braced substring: If you intended to parameterize the question with a Scenario this should be changed to a double-braced substring, e.g. {{variable}}.\nSee details on constructing Scenarios in the docs: https://docs.expectedparrot.com/en/latest/scenarios.html",
|
338
374
|
UserWarning,
|
339
375
|
)
|
376
|
+
|
377
|
+
|
378
|
+
if __name__ == "__main__":
|
379
|
+
import doctest
|
380
|
+
|
381
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
"""This module provides a factory class for creating question objects."""
|
2
2
|
|
3
3
|
import textwrap
|
4
|
-
from
|
4
|
+
from uuid import UUID
|
5
|
+
from typing import Any, Optional, Union
|
6
|
+
|
5
7
|
|
6
|
-
from edsl.exceptions import QuestionSerializationError
|
7
|
-
from edsl.exceptions import QuestionCreationValidationError
|
8
8
|
from edsl.questions.QuestionBase import RegisterQuestionsMeta
|
9
9
|
|
10
10
|
|
@@ -60,46 +60,35 @@ class Question(metaclass=Meta):
|
|
60
60
|
return q.example()
|
61
61
|
|
62
62
|
@classmethod
|
63
|
-
def pull(cls,
|
63
|
+
def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
|
64
64
|
"""Pull the object from coop."""
|
65
65
|
from edsl.coop import Coop
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
id = id_or_url.split("/")[-1]
|
70
|
-
else:
|
71
|
-
id = id_or_url
|
72
|
-
from edsl.questions.QuestionBase import QuestionBase
|
73
|
-
|
74
|
-
return c._get_base(QuestionBase, id)
|
67
|
+
coop = Coop()
|
68
|
+
return coop.get(uuid, url, "question")
|
75
69
|
|
76
70
|
@classmethod
|
77
|
-
def delete(cls,
|
71
|
+
def delete(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
|
78
72
|
"""Delete the object from coop."""
|
79
73
|
from edsl.coop import Coop
|
80
74
|
|
81
|
-
|
82
|
-
|
83
|
-
id = id_or_url.split("/")[-1]
|
84
|
-
else:
|
85
|
-
id = id_or_url
|
86
|
-
from edsl.questions.QuestionBase import QuestionBase
|
87
|
-
|
88
|
-
return c._delete_base(QuestionBase, id)
|
75
|
+
coop = Coop()
|
76
|
+
return coop.delete(uuid, url)
|
89
77
|
|
90
78
|
@classmethod
|
91
|
-
def
|
92
|
-
|
79
|
+
def patch(
|
80
|
+
cls,
|
81
|
+
uuid: Optional[Union[str, UUID]] = None,
|
82
|
+
url: Optional[str] = None,
|
83
|
+
description: Optional[str] = None,
|
84
|
+
value: Optional[Any] = None,
|
85
|
+
visibility: Optional[str] = None,
|
86
|
+
):
|
87
|
+
"""Patch the object on coop."""
|
93
88
|
from edsl.coop import Coop
|
94
89
|
|
95
|
-
|
96
|
-
|
97
|
-
id = id_or_url.split("/")[-1]
|
98
|
-
else:
|
99
|
-
id = id_or_url
|
100
|
-
from edsl.questions.QuestionBase import QuestionBase
|
101
|
-
|
102
|
-
return c._update_base(QuestionBase, id, visibility)
|
90
|
+
coop = Coop()
|
91
|
+
return coop.patch(uuid, url, description, value, visibility)
|
103
92
|
|
104
93
|
@classmethod
|
105
94
|
def available(cls, show_class_names: bool = False) -> Union[list, dict]:
|
edsl/questions/settings.py
CHANGED
edsl/results/Dataset.py
CHANGED
@@ -78,6 +78,28 @@ class Dataset(UserList, ResultsExportMixin):
|
|
78
78
|
|
79
79
|
return get_values(self.data[0])[0]
|
80
80
|
|
81
|
+
def select(self, *keys):
|
82
|
+
"""Return a new dataset with only the selected keys.
|
83
|
+
|
84
|
+
:param keys: The keys to select.
|
85
|
+
|
86
|
+
>>> d = Dataset([{'a.b':[1,2,3,4]}, {'c.d':[5,6,7,8]}])
|
87
|
+
>>> d.select('a.b')
|
88
|
+
Dataset([{'a.b': [1, 2, 3, 4]}])
|
89
|
+
|
90
|
+
>>> d.select('a.b', 'c.d')
|
91
|
+
Dataset([{'a.b': [1, 2, 3, 4]}, {'c.d': [5, 6, 7, 8]}])
|
92
|
+
"""
|
93
|
+
if isinstance(keys, str):
|
94
|
+
keys = [keys]
|
95
|
+
|
96
|
+
new_data = []
|
97
|
+
for observation in self.data:
|
98
|
+
observation_key = list(observation.keys())[0]
|
99
|
+
if observation_key in keys:
|
100
|
+
new_data.append(observation)
|
101
|
+
return Dataset(new_data)
|
102
|
+
|
81
103
|
def _repr_html_(self) -> str:
|
82
104
|
"""Return an HTML representation of the dataset."""
|
83
105
|
from edsl.utilities.utilities import data_to_html
|
@@ -223,6 +245,15 @@ class Dataset(UserList, ResultsExportMixin):
|
|
223
245
|
|
224
246
|
return Dataset(new_data)
|
225
247
|
|
248
|
+
@classmethod
|
249
|
+
def example(self):
|
250
|
+
"""Return an example dataset.
|
251
|
+
|
252
|
+
>>> Dataset.example()
|
253
|
+
Dataset([{'a': [1, 2, 3, 4]}, {'b': [4, 3, 2, 1]}])
|
254
|
+
"""
|
255
|
+
return Dataset([{"a": [1, 2, 3, 4]}, {"b": [4, 3, 2, 1]}])
|
256
|
+
|
226
257
|
|
227
258
|
if __name__ == "__main__":
|
228
259
|
import doctest
|