edsl 0.1.28__py3-none-any.whl → 0.1.29__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.
Files changed (76) hide show
  1. edsl/Base.py +18 -18
  2. edsl/__init__.py +24 -24
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +77 -41
  5. edsl/agents/AgentList.py +35 -6
  6. edsl/agents/Invigilator.py +19 -1
  7. edsl/agents/InvigilatorBase.py +15 -10
  8. edsl/agents/PromptConstructionMixin.py +342 -100
  9. edsl/agents/descriptors.py +2 -1
  10. edsl/base/Base.py +289 -0
  11. edsl/config.py +2 -1
  12. edsl/conjure/InputData.py +39 -8
  13. edsl/coop/coop.py +188 -151
  14. edsl/coop/utils.py +43 -75
  15. edsl/data/Cache.py +19 -5
  16. edsl/data/SQLiteDict.py +11 -3
  17. edsl/jobs/Answers.py +15 -1
  18. edsl/jobs/Jobs.py +92 -47
  19. edsl/jobs/buckets/ModelBuckets.py +4 -2
  20. edsl/jobs/buckets/TokenBucket.py +1 -2
  21. edsl/jobs/interviews/Interview.py +3 -9
  22. edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
  23. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +15 -10
  24. edsl/jobs/runners/JobsRunnerAsyncio.py +21 -25
  25. edsl/jobs/tasks/TaskHistory.py +4 -3
  26. edsl/language_models/LanguageModel.py +5 -11
  27. edsl/language_models/ModelList.py +3 -3
  28. edsl/language_models/repair.py +8 -7
  29. edsl/notebooks/Notebook.py +40 -3
  30. edsl/prompts/Prompt.py +31 -19
  31. edsl/questions/QuestionBase.py +38 -13
  32. edsl/questions/QuestionBudget.py +5 -6
  33. edsl/questions/QuestionCheckBox.py +7 -3
  34. edsl/questions/QuestionExtract.py +5 -3
  35. edsl/questions/QuestionFreeText.py +3 -3
  36. edsl/questions/QuestionFunctional.py +0 -3
  37. edsl/questions/QuestionList.py +3 -4
  38. edsl/questions/QuestionMultipleChoice.py +16 -8
  39. edsl/questions/QuestionNumerical.py +4 -3
  40. edsl/questions/QuestionRank.py +5 -3
  41. edsl/questions/__init__.py +4 -3
  42. edsl/questions/descriptors.py +4 -2
  43. edsl/questions/question_registry.py +20 -31
  44. edsl/questions/settings.py +1 -1
  45. edsl/results/Dataset.py +31 -0
  46. edsl/results/DatasetExportMixin.py +493 -0
  47. edsl/results/Result.py +22 -74
  48. edsl/results/Results.py +105 -67
  49. edsl/results/ResultsDBMixin.py +7 -3
  50. edsl/results/ResultsExportMixin.py +22 -537
  51. edsl/results/ResultsGGMixin.py +3 -3
  52. edsl/results/ResultsToolsMixin.py +5 -5
  53. edsl/scenarios/FileStore.py +140 -0
  54. edsl/scenarios/Scenario.py +5 -6
  55. edsl/scenarios/ScenarioList.py +44 -15
  56. edsl/scenarios/ScenarioListExportMixin.py +32 -0
  57. edsl/scenarios/ScenarioListPdfMixin.py +2 -1
  58. edsl/scenarios/__init__.py +1 -0
  59. edsl/study/ObjectEntry.py +89 -13
  60. edsl/study/ProofOfWork.py +5 -2
  61. edsl/study/SnapShot.py +4 -8
  62. edsl/study/Study.py +21 -14
  63. edsl/study/__init__.py +2 -0
  64. edsl/surveys/MemoryPlan.py +11 -4
  65. edsl/surveys/Survey.py +46 -7
  66. edsl/surveys/SurveyExportMixin.py +4 -2
  67. edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
  68. edsl/tools/plotting.py +4 -2
  69. edsl/utilities/__init__.py +21 -21
  70. edsl/utilities/interface.py +66 -45
  71. edsl/utilities/utilities.py +11 -13
  72. {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/METADATA +11 -10
  73. {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/RECORD +75 -72
  74. edsl-0.1.28.dist-info/entry_points.txt +0 -3
  75. {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/LICENSE +0 -0
  76. {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/WHEEL +0 -0
@@ -1,8 +1,8 @@
1
1
  """This module contains the Question class, which is the base class for all questions in EDSL."""
2
2
 
3
3
  from __future__ import annotations
4
+ import time
4
5
  from abc import ABC, abstractmethod
5
- from rich.table import Table
6
6
  from typing import Any, Type, Optional, List, Callable
7
7
  import copy
8
8
 
@@ -12,7 +12,7 @@ from edsl.exceptions import (
12
12
  )
13
13
  from edsl.questions.descriptors import QuestionNameDescriptor, QuestionTextDescriptor
14
14
 
15
- from edsl.prompts.registry import get_classes as prompt_lookup
15
+
16
16
  from edsl.questions.AnswerValidatorMixin import AnswerValidatorMixin
17
17
  from edsl.questions.RegisterQuestionsMeta import RegisterQuestionsMeta
18
18
  from edsl.Base import PersistenceMixin, RichPrintingMixin
@@ -124,6 +124,8 @@ class QuestionBase(
124
124
  :param model: The language model to use. If None, assumes does not matter.
125
125
 
126
126
  """
127
+ from edsl.prompts.registry import get_classes as prompt_lookup
128
+
127
129
  applicable_prompts = prompt_lookup(
128
130
  component_type="question_instructions",
129
131
  question_type=cls.question_type,
@@ -173,15 +175,16 @@ class QuestionBase(
173
175
  def add_model_instructions(
174
176
  self, *, instructions: str, model: Optional[str] = None
175
177
  ) -> None:
176
- """Add model-specific instructions for the question.
178
+ """Add model-specific instructions for the question that override the default instructions.
177
179
 
178
180
  :param instructions: The instructions to add. This is typically a jinja2 template.
179
181
  :param model: The language model for this instruction.
180
182
 
181
183
  >>> from edsl.questions import QuestionFreeText
182
184
  >>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
183
- >>> q.add_model_instructions(instructions = "Answer in valid JSON like so {'answer': 'comment: <>}", model = "gpt3")
184
-
185
+ >>> q.add_model_instructions(instructions = "{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}", model = "gpt3")
186
+ >>> q.get_instructions(model = "gpt3")
187
+ Prompt(text=\"""{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}\""")
185
188
  """
186
189
  from edsl import Model
187
190
 
@@ -201,6 +204,13 @@ class QuestionBase(
201
204
  """Get the mathcing question-answering instructions for the question.
202
205
 
203
206
  :param model: The language model to use.
207
+
208
+ >>> from edsl import QuestionFreeText
209
+ >>> QuestionFreeText.example().get_instructions()
210
+ Prompt(text=\"""You are being asked the following question: {{question_text}}
211
+ Return a valid JSON formatted like this:
212
+ {"answer": "<put free text answer here>"}
213
+ \""")
204
214
  """
205
215
  from edsl.prompts.Prompt import Prompt
206
216
 
@@ -293,7 +303,16 @@ class QuestionBase(
293
303
  print_json(json.dumps(self.to_dict()))
294
304
 
295
305
  def __call__(self, just_answer=True, model=None, agent=None, **kwargs):
296
- """Call the question."""
306
+ """Call the question.
307
+
308
+ >>> from edsl.language_models import LanguageModel
309
+ >>> m = LanguageModel.example(canned_response = "Yo, what's up?", test_model = True)
310
+ >>> from edsl import QuestionFreeText
311
+ >>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
312
+ >>> q(model = m)
313
+ "Yo, what's up?"
314
+
315
+ """
297
316
  survey = self.to_survey()
298
317
  results = survey(model=model, agent=agent, **kwargs)
299
318
  if just_answer:
@@ -304,7 +323,6 @@ class QuestionBase(
304
323
  async def run_async(self, just_answer=True, model=None, agent=None, **kwargs):
305
324
  """Call the question."""
306
325
  survey = self.to_survey()
307
- ## asyncio.run(survey.async_call());
308
326
  results = await survey.run_async(model=model, agent=agent, **kwargs)
309
327
  if just_answer:
310
328
  return results.select(f"answer.{self.question_name}").first()
@@ -383,29 +401,34 @@ class QuestionBase(
383
401
  s = Survey([self, other])
384
402
  return s
385
403
 
386
- def to_survey(self):
404
+ def to_survey(self) -> "Survey":
387
405
  """Turn a single question into a survey."""
388
406
  from edsl.surveys.Survey import Survey
389
407
 
390
408
  s = Survey([self])
391
409
  return s
392
410
 
393
- def run(self, *args, **kwargs):
411
+ def run(self, *args, **kwargs) -> "Results":
394
412
  """Turn a single question into a survey and run it."""
395
413
  from edsl.surveys.Survey import Survey
396
414
 
397
415
  s = self.to_survey()
398
416
  return s.run(*args, **kwargs)
399
417
 
400
- def by(self, *args):
401
- """Turn a single question into a survey and run it."""
418
+ def by(self, *args) -> "Jobs":
419
+ """Turn a single question into a survey and then a Job."""
402
420
  from edsl.surveys.Survey import Survey
403
421
 
404
422
  s = Survey([self])
405
423
  return s.by(*args)
406
424
 
407
- def human_readable(self):
408
- """Print the question in a human readable format."""
425
+ def human_readable(self) -> str:
426
+ """Print the question in a human readable format.
427
+
428
+ >>> from edsl.questions import QuestionFreeText
429
+ >>> QuestionFreeText.example().human_readable()
430
+ 'Question Type: free_text\\nQuestion: How are you?'
431
+ """
409
432
  lines = []
410
433
  lines.append(f"Question Type: {self.question_type}")
411
434
  lines.append(f"Question: {self.question_text}")
@@ -475,6 +498,8 @@ class QuestionBase(
475
498
 
476
499
  def rich_print(self):
477
500
  """Print the question in a rich format."""
501
+ from rich.table import Table
502
+
478
503
  table = Table(show_header=True, header_style="bold magenta")
479
504
  table.add_column("Question Name", style="dim")
480
505
  table.add_column("Question Type")
@@ -1,11 +1,8 @@
1
1
  from __future__ import annotations
2
2
  import random
3
- import textwrap
4
3
  from typing import Any, Optional, Union
5
4
  from edsl.questions.QuestionBase import QuestionBase
6
5
  from edsl.questions.descriptors import IntegerDescriptor, QuestionOptionsDescriptor
7
- from edsl.scenarios import Scenario
8
- from edsl.utilities import random_string
9
6
 
10
7
 
11
8
  class QuestionBudget(QuestionBase):
@@ -46,7 +43,7 @@ class QuestionBudget(QuestionBase):
46
43
  return answer
47
44
 
48
45
  def _translate_answer_code_to_answer(
49
- self, answer_codes: dict[str, int], scenario: Scenario = None
46
+ self, answer_codes: dict[str, int], scenario: "Scenario" = None
50
47
  ):
51
48
  """
52
49
  Translate the answer codes to the actual answers.
@@ -63,6 +60,8 @@ class QuestionBudget(QuestionBase):
63
60
 
64
61
  def _simulate_answer(self, human_readable=True):
65
62
  """Simulate a valid answer for debugging purposes (what the validator expects)."""
63
+ from edsl.utilities.utilities import random_string
64
+
66
65
  if human_readable:
67
66
  keys = self.question_options
68
67
  else:
@@ -163,8 +162,8 @@ def main():
163
162
 
164
163
 
165
164
  if __name__ == "__main__":
166
- q = QuestionBudget.example()
167
- results = q.run()
165
+ # q = QuestionBudget.example()
166
+ # results = q.run()
168
167
 
169
168
  import doctest
170
169
 
@@ -9,8 +9,6 @@ from edsl.questions.descriptors import (
9
9
  IntegerDescriptor,
10
10
  QuestionOptionsDescriptor,
11
11
  )
12
- from edsl.scenarios import Scenario
13
- from edsl.utilities import random_string
14
12
 
15
13
 
16
14
  class QuestionCheckBox(QuestionBase):
@@ -55,13 +53,17 @@ class QuestionCheckBox(QuestionBase):
55
53
  self._validate_answer_checkbox(answer)
56
54
  return answer
57
55
 
58
- def _translate_answer_code_to_answer(self, answer_codes, scenario: Scenario = None):
56
+ def _translate_answer_code_to_answer(
57
+ self, answer_codes, scenario: "Scenario" = None
58
+ ):
59
59
  """
60
60
  Translate the answer code to the actual answer.
61
61
 
62
62
  For example, for question options ["a", "b", "c"],the answer codes are 0, 1, and 2.
63
63
  The LLM will respond with [0,1] and this code will translate it to ["a","b"].
64
64
  """
65
+ from edsl.scenarios.Scenario import Scenario
66
+
65
67
  scenario = scenario or Scenario()
66
68
  translated_options = [
67
69
  Template(option).render(scenario) for option in self.question_options
@@ -73,6 +75,8 @@ class QuestionCheckBox(QuestionBase):
73
75
 
74
76
  def _simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
75
77
  """Simulate a valid answer for debugging purposes."""
78
+ from edsl.utilities.utilities import random_string
79
+
76
80
  min_selections = self.min_selections or 1
77
81
  max_selections = self.max_selections or len(self.question_options)
78
82
  num_selections = random.randint(min_selections, max_selections)
@@ -2,8 +2,6 @@ from __future__ import annotations
2
2
  from typing import Any
3
3
  from edsl.questions.QuestionBase import QuestionBase
4
4
  from edsl.questions.descriptors import AnswerTemplateDescriptor
5
- from edsl.scenarios import Scenario
6
- from edsl.utilities import random_string
7
5
 
8
6
 
9
7
  class QuestionExtract(QuestionBase):
@@ -44,12 +42,14 @@ class QuestionExtract(QuestionBase):
44
42
  self._validate_answer_extract(answer)
45
43
  return answer
46
44
 
47
- def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
45
+ def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
48
46
  """Return the answer in a human-readable format."""
49
47
  return answer
50
48
 
51
49
  def _simulate_answer(self, human_readable: bool = True) -> dict[str, str]:
52
50
  """Simulate a valid answer for debugging purposes."""
51
+ from edsl.utilities.utilities import random_string
52
+
53
53
  return {
54
54
  "answer": {key: random_string() for key in self.answer_template.keys()},
55
55
  "comment": random_string(),
@@ -106,6 +106,8 @@ def main():
106
106
  q.to_dict()
107
107
  assert q.from_dict(q.to_dict()) == q
108
108
 
109
+
110
+ if __name__ == "__main__":
109
111
  import doctest
110
112
 
111
113
  doctest.testmod(optionflags=doctest.ELLIPSIS)
@@ -2,8 +2,6 @@ from __future__ import annotations
2
2
  import textwrap
3
3
  from typing import Any, Optional
4
4
  from edsl.questions.QuestionBase import QuestionBase
5
- from edsl.scenarios import Scenario
6
- from edsl.utilities import random_string
7
5
 
8
6
 
9
7
  class QuestionFreeText(QuestionBase):
@@ -43,12 +41,14 @@ class QuestionFreeText(QuestionBase):
43
41
  self._validate_answer_key_value(answer, "answer", str)
44
42
  return answer
45
43
 
46
- def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
44
+ def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
47
45
  """Do nothing, because the answer is already in a human-readable format."""
48
46
  return answer
49
47
 
50
48
  def _simulate_answer(self, human_readable: bool = True) -> dict[str, str]:
51
49
  """Simulate a valid answer for debugging purposes."""
50
+ from edsl.utilities.utilities import random_string
51
+
52
52
  return {"answer": random_string()}
53
53
 
54
54
  @property
@@ -1,10 +1,7 @@
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
10
7
 
@@ -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,13 +1,12 @@
1
1
  from __future__ import annotations
2
- from typing import Optional, Union
2
+ import time
3
+ from typing import Union
3
4
  import random
4
5
 
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.scenarios import Scenario
9
+ from edsl.questions.descriptors import QuestionOptionsDescriptor
11
10
 
12
11
 
13
12
  class QuestionMultipleChoice(QuestionBase):
@@ -15,9 +14,9 @@ class QuestionMultipleChoice(QuestionBase):
15
14
 
16
15
  question_type = "multiple_choice"
17
16
  purpose = "When options are known and limited"
18
- question_options: Union[
19
- list[str], list[list], list[float], list[int]
20
- ] = QuestionOptionsDescriptor()
17
+ question_options: Union[list[str], list[list], list[float], list[int]] = (
18
+ QuestionOptionsDescriptor()
19
+ )
21
20
 
22
21
  def __init__(
23
22
  self,
@@ -47,8 +46,12 @@ class QuestionMultipleChoice(QuestionBase):
47
46
  self._validate_answer_multiple_choice(answer)
48
47
  return answer
49
48
 
50
- def _translate_answer_code_to_answer(self, answer_code, scenario: Scenario = None):
49
+ def _translate_answer_code_to_answer(
50
+ self, answer_code, scenario: "Scenario" = None
51
+ ):
51
52
  """Translate the answer code to the actual answer."""
53
+ from edsl.scenarios.Scenario import Scenario
54
+
52
55
  scenario = scenario or Scenario()
53
56
  translated_options = [
54
57
  Template(str(option)).render(scenario) for option in self.question_options
@@ -59,6 +62,8 @@ class QuestionMultipleChoice(QuestionBase):
59
62
  self, human_readable: bool = True
60
63
  ) -> dict[str, Union[int, str]]:
61
64
  """Simulate a valid answer for debugging purposes."""
65
+ from edsl.utilities.utilities import random_string
66
+
62
67
  if human_readable:
63
68
  answer = random.choice(self.question_options)
64
69
  else:
@@ -70,6 +75,7 @@ class QuestionMultipleChoice(QuestionBase):
70
75
 
71
76
  @property
72
77
  def question_html_content(self) -> str:
78
+
73
79
  if hasattr(self, "option_labels"):
74
80
  option_labels = self.option_labels
75
81
  else:
@@ -127,6 +133,8 @@ def main():
127
133
  q.to_dict()
128
134
  assert q.from_dict(q.to_dict()) == q
129
135
 
136
+
137
+ if __name__ == "__main__":
130
138
  import doctest
131
139
 
132
140
  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(),
@@ -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
- from edsl.scenarios import Scenario
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:
@@ -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
@@ -8,9 +8,7 @@ from edsl.exceptions import (
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})."
@@ -1,10 +1,10 @@
1
1
  """This module provides a factory class for creating question objects."""
2
2
 
3
3
  import textwrap
4
- from typing import Union
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, id_or_url: str):
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
- c = Coop()
68
- if c.url in id_or_url:
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, id_or_url: str):
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
- c = Coop()
82
- if c.url in id_or_url:
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 update(cls, id_or_url: str, visibility: str):
92
- """Update the object on coop."""
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
- c = Coop()
96
- if c.url in id_or_url:
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]:
@@ -8,5 +8,5 @@ class Settings:
8
8
  MAX_EXPRESSION_CONSTRAINT_LENGTH = 1000
9
9
  MAX_NUM_OPTIONS = 200
10
10
  MIN_NUM_OPTIONS = 2
11
- MAX_OPTION_LENGTH = 1000
11
+ MAX_OPTION_LENGTH = 10000
12
12
  MAX_QUESTION_LENGTH = 100000
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