edsl 0.1.29__py3-none-any.whl → 0.1.29.dev1__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 +41 -77
  5. edsl/agents/AgentList.py +6 -35
  6. edsl/agents/Invigilator.py +1 -19
  7. edsl/agents/InvigilatorBase.py +10 -15
  8. edsl/agents/PromptConstructionMixin.py +100 -342
  9. edsl/agents/descriptors.py +1 -2
  10. edsl/config.py +1 -2
  11. edsl/conjure/InputData.py +8 -39
  12. edsl/coop/coop.py +150 -187
  13. edsl/coop/utils.py +75 -43
  14. edsl/data/Cache.py +5 -19
  15. edsl/data/SQLiteDict.py +3 -11
  16. edsl/jobs/Answers.py +1 -15
  17. edsl/jobs/Jobs.py +46 -90
  18. edsl/jobs/buckets/ModelBuckets.py +2 -4
  19. edsl/jobs/buckets/TokenBucket.py +2 -1
  20. edsl/jobs/interviews/Interview.py +9 -3
  21. edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
  22. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +10 -15
  23. edsl/jobs/runners/JobsRunnerAsyncio.py +25 -21
  24. edsl/jobs/tasks/TaskHistory.py +3 -4
  25. edsl/language_models/LanguageModel.py +11 -5
  26. edsl/language_models/ModelList.py +3 -3
  27. edsl/language_models/repair.py +7 -8
  28. edsl/notebooks/Notebook.py +3 -40
  29. edsl/prompts/Prompt.py +19 -31
  30. edsl/questions/QuestionBase.py +13 -38
  31. edsl/questions/QuestionBudget.py +6 -5
  32. edsl/questions/QuestionCheckBox.py +3 -7
  33. edsl/questions/QuestionExtract.py +3 -5
  34. edsl/questions/QuestionFreeText.py +3 -3
  35. edsl/questions/QuestionFunctional.py +3 -0
  36. edsl/questions/QuestionList.py +4 -3
  37. edsl/questions/QuestionMultipleChoice.py +8 -16
  38. edsl/questions/QuestionNumerical.py +3 -4
  39. edsl/questions/QuestionRank.py +3 -5
  40. edsl/questions/__init__.py +3 -4
  41. edsl/questions/descriptors.py +2 -4
  42. edsl/questions/question_registry.py +31 -20
  43. edsl/questions/settings.py +1 -1
  44. edsl/results/Dataset.py +0 -31
  45. edsl/results/Result.py +74 -22
  46. edsl/results/Results.py +47 -97
  47. edsl/results/ResultsDBMixin.py +3 -7
  48. edsl/results/ResultsExportMixin.py +537 -22
  49. edsl/results/ResultsGGMixin.py +3 -3
  50. edsl/results/ResultsToolsMixin.py +5 -5
  51. edsl/scenarios/Scenario.py +6 -5
  52. edsl/scenarios/ScenarioList.py +11 -34
  53. edsl/scenarios/ScenarioListPdfMixin.py +1 -2
  54. edsl/scenarios/__init__.py +0 -1
  55. edsl/study/ObjectEntry.py +13 -89
  56. edsl/study/ProofOfWork.py +2 -5
  57. edsl/study/SnapShot.py +8 -4
  58. edsl/study/Study.py +14 -21
  59. edsl/study/__init__.py +0 -2
  60. edsl/surveys/MemoryPlan.py +4 -11
  61. edsl/surveys/Survey.py +7 -46
  62. edsl/surveys/SurveyExportMixin.py +2 -4
  63. edsl/surveys/SurveyFlowVisualizationMixin.py +4 -6
  64. edsl/tools/plotting.py +2 -4
  65. edsl/utilities/__init__.py +21 -21
  66. edsl/utilities/interface.py +45 -66
  67. edsl/utilities/utilities.py +13 -11
  68. {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/METADATA +10 -11
  69. {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/RECORD +72 -75
  70. edsl-0.1.29.dev1.dist-info/entry_points.txt +3 -0
  71. edsl/base/Base.py +0 -289
  72. edsl/results/DatasetExportMixin.py +0 -493
  73. edsl/scenarios/FileStore.py +0 -140
  74. edsl/scenarios/ScenarioListExportMixin.py +0 -32
  75. {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/LICENSE +0 -0
  76. {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/WHEEL +0 -0
@@ -5,22 +5,25 @@ import csv
5
5
  import random
6
6
  from collections import UserList, Counter
7
7
  from collections.abc import Iterable
8
+
8
9
  from typing import Any, Optional, Union, List
9
10
 
11
+ from rich.table import Table
10
12
  from simpleeval import EvalWithCompoundTypes
11
13
 
14
+ from edsl.scenarios.Scenario import Scenario
12
15
  from edsl.Base import Base
13
16
  from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
14
- from edsl.scenarios.Scenario import Scenario
15
17
  from edsl.scenarios.ScenarioListPdfMixin import ScenarioListPdfMixin
16
- from edsl.scenarios.ScenarioListExportMixin import ScenarioListExportMixin
17
18
 
19
+ from edsl.utilities.interface import print_scenario_list
18
20
 
19
- class ScenarioListMixin(ScenarioListPdfMixin, ScenarioListExportMixin):
20
- pass
21
+ from edsl.utilities import is_valid_variable_name
21
22
 
23
+ from edsl.results.ResultsExportMixin import ResultsExportMixin
22
24
 
23
- class ScenarioList(Base, UserList, ScenarioListMixin):
25
+
26
+ class ScenarioList(Base, UserList, ScenarioListPdfMixin, ResultsExportMixin):
24
27
  """Class for creating a list of scenarios to be used in a survey."""
25
28
 
26
29
  def __init__(self, data: Optional[list] = None):
@@ -116,7 +119,7 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
116
119
 
117
120
  return ScenarioList(random.sample(self.data, n))
118
121
 
119
- def expand(self, expand_field: str, number_field=False) -> ScenarioList:
122
+ def expand(self, expand_field: str, number_field = False) -> ScenarioList:
120
123
  """Expand the ScenarioList by a field.
121
124
 
122
125
  Example:
@@ -134,7 +137,7 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
134
137
  new_scenario = scenario.copy()
135
138
  new_scenario[expand_field] = value
136
139
  if number_field:
137
- new_scenario[expand_field + "_number"] = index + 1
140
+ new_scenario[expand_field + '_number'] = index + 1
138
141
  new_scenarios.append(new_scenario)
139
142
  return ScenarioList(new_scenarios)
140
143
 
@@ -154,8 +157,6 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
154
157
  )
155
158
  raw_var_name, expression = new_var_string.split("=", 1)
156
159
  var_name = raw_var_name.strip()
157
- from edsl.utilities.utilities import is_valid_variable_name
158
-
159
160
  if not is_valid_variable_name(var_name):
160
161
  raise Exception(f"{var_name} is not a valid variable name.")
161
162
 
@@ -191,7 +192,7 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
191
192
 
192
193
  def get_sort_key(scenario: Any) -> tuple:
193
194
  return tuple(scenario[field] for field in fields)
194
-
195
+
195
196
  return ScenarioList(sorted(self, key=get_sort_key, reverse=reverse))
196
197
 
197
198
  def filter(self, expression: str) -> ScenarioList:
@@ -342,20 +343,6 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
342
343
  """
343
344
  return cls([Scenario(row) for row in df.to_dict(orient="records")])
344
345
 
345
- def to_key_value(self, field, value=None) -> Union[dict, set]:
346
- """Return the set of values in the field.
347
-
348
- Example:
349
-
350
- >>> s = ScenarioList([Scenario({'name': 'Alice'}), Scenario({'name': 'Bob'})])
351
- >>> s.to_key_value('name') == {'Alice', 'Bob'}
352
- True
353
- """
354
- if value is None:
355
- return {scenario[field] for scenario in self}
356
- else:
357
- return {scenario[field]: scenario[value] for scenario in self}
358
-
359
346
  @classmethod
360
347
  def from_csv(cls, filename: str) -> ScenarioList:
361
348
  """Create a ScenarioList from a CSV file.
@@ -375,8 +362,6 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
375
362
  >>> scenario_list[1]['age']
376
363
  '25'
377
364
  """
378
- from edsl.scenarios.Scenario import Scenario
379
-
380
365
  observations = []
381
366
  with open(filename, "r") as f:
382
367
  reader = csv.reader(f)
@@ -414,16 +399,12 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
414
399
  ScenarioList([Scenario({'name': 'Alice'}), Scenario({'name': 'Bob'})])
415
400
 
416
401
  """
417
- from edsl.scenarios.Scenario import Scenario
418
-
419
402
  return cls([Scenario(s) for s in scenario_dicts_list])
420
403
 
421
404
  @classmethod
422
405
  @remove_edsl_version
423
406
  def from_dict(cls, data) -> ScenarioList:
424
407
  """Create a `ScenarioList` from a dictionary."""
425
- from edsl.scenarios.Scenario import Scenario
426
-
427
408
  return cls([Scenario.from_dict(s) for s in data["scenarios"]])
428
409
 
429
410
  def code(self) -> str:
@@ -448,8 +429,6 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
448
429
 
449
430
  def rich_print(self) -> None:
450
431
  """Display an object as a table."""
451
- from rich.table import Table
452
-
453
432
  table = Table(title="ScenarioList")
454
433
  table.add_column("Index", style="bold")
455
434
  table.add_column("Scenario")
@@ -464,8 +443,6 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
464
443
  pretty_labels: Optional[dict] = None,
465
444
  filename: str = None,
466
445
  ):
467
- from edsl.utilities.interface import print_scenario_list
468
-
469
446
  print_scenario_list(self[:max_rows])
470
447
 
471
448
  def __getitem__(self, key: Union[int, slice]) -> Any:
@@ -2,7 +2,7 @@ import fitz # PyMuPDF
2
2
  import os
3
3
  import subprocess
4
4
 
5
- # from edsl import Scenario
5
+ from edsl import Scenario
6
6
 
7
7
 
8
8
  class ScenarioListPdfMixin:
@@ -22,7 +22,6 @@ class ScenarioListPdfMixin:
22
22
  """
23
23
  import tempfile
24
24
  from pdf2image import convert_from_path
25
- from edsl.scenarios import Scenario
26
25
 
27
26
  with tempfile.TemporaryDirectory() as output_folder:
28
27
  # Convert PDF to images
@@ -1,2 +1 @@
1
1
  from edsl.scenarios.Scenario import Scenario
2
- from edsl.scenarios.ScenarioList import ScenarioList
edsl/study/ObjectEntry.py CHANGED
@@ -1,8 +1,5 @@
1
1
  import time
2
- import webbrowser
3
- from typing import Any, Dict, Optional, Type
4
- from edsl import QuestionBase
5
- from edsl.Base import RegisterSubclassesMeta
2
+ from typing import Optional, Dict, Any, Type
6
3
 
7
4
 
8
5
  class ObjectEntry:
@@ -15,16 +12,6 @@ class ObjectEntry:
15
12
  created_at: Optional[float] = None,
16
13
  edsl_class_name: Optional[str] = None,
17
14
  ):
18
- """
19
- Initialize an ObjectEntry instance.
20
-
21
- :param variable_name: The name of the variable.
22
- :param object: The object being wrapped.
23
- :param description: A description of the object.
24
- :param coop_info: Optional Coop information dictionary.
25
- :param created_at: Optional creation timestamp. Defaults to current time.
26
- :param edsl_class_name: Optional EDSL class name. Defaults to object's class name.
27
- """
28
15
  self.created_at = created_at or time.time()
29
16
  self.variable_name = variable_name
30
17
  self.object = object
@@ -33,33 +20,22 @@ class ObjectEntry:
33
20
  self.coop_info = coop_info
34
21
 
35
22
  @classmethod
36
- def _get_class(cls, object_dict: Dict[str, Any]) -> Type:
37
- """
38
- Get the class of an object from its dictionary representation.
39
-
40
- :param object_dict: The dictionary representation of the object.
41
- :return: The class of the object.
42
- """
43
- class_name = object_dict["edsl_class_name"]
23
+ def _get_class(self, obj_dict: Dict[str, Any]) -> Type:
24
+ "Get the class of an object from its dictionary representation."
25
+ class_name = obj_dict["edsl_class_name"]
44
26
  if class_name == "QuestionBase":
27
+ from edsl import QuestionBase
28
+
45
29
  return QuestionBase
46
30
  else:
31
+ from edsl.Base import RegisterSubclassesMeta
32
+
47
33
  return RegisterSubclassesMeta._registry[class_name]
48
34
 
49
35
  def __repr__(self) -> str:
50
- """
51
- Return a string representation of the ObjectEntry instance.
52
-
53
- :return: A string representation of the ObjectEntry instance.
54
- """
55
36
  return f"ObjectEntry(variable_name='{self.variable_name}', object={self.object!r}, description='{self.description}', coop_info={self.coop_info}, created_at={self.created_at}, edsl_class_name='{self.edsl_class_name}')"
56
37
 
57
38
  def to_dict(self) -> Dict[str, Any]:
58
- """
59
- Convert the ObjectEntry instance to a dictionary.
60
-
61
- :return: A dictionary representation of the ObjectEntry instance.
62
- """
63
39
  return {
64
40
  "created_at": self.created_at,
65
41
  "variable_name": self.variable_name,
@@ -71,65 +47,34 @@ class ObjectEntry:
71
47
 
72
48
  @classmethod
73
49
  def from_dict(cls, d: Dict[str, Any]) -> "ObjectEntry":
74
- """
75
- Create an ObjectEntry instance from a dictionary.
76
-
77
- :param d: The dictionary representation of the ObjectEntry instance.
78
- :return: An ObjectEntry instance.
79
- """
80
50
  d["object"] = cls._get_class(d["object"]).from_dict(d["object"])
81
51
  return cls(**d)
82
52
 
83
53
  @property
84
54
  def hash(self) -> str:
85
- """
86
- Compute the hash of the object.
87
-
88
- :return: The hash of the object as a string.
89
- """
90
55
  return str(hash(self.object))
91
56
 
92
57
  def add_to_namespace(self) -> None:
93
- """
94
- Add the object to the global namespace using its variable name.
95
- """
96
58
  globals()[self.variable_name] = self.object
97
59
 
98
60
  @property
99
61
  def coop_info(self) -> Optional[Dict[str, Any]]:
100
- """
101
- Get the Coop information for the object.
102
-
103
- :return: The Coop information dictionary, if available.
104
- """
105
62
  return self._coop_info
106
63
 
107
64
  @coop_info.setter
108
65
  def coop_info(self, coop_info: Optional[Dict[str, Any]]) -> None:
109
- """
110
- Set the Coop information for the object.
111
-
112
- :param coop_info: The Coop information dictionary.
113
- """
114
66
  self._coop_info = coop_info
115
67
 
116
68
  def view_on_coop(self) -> None:
117
- """
118
- Open the object's Coop URL in a web browser.
119
- """
120
69
  if self.coop_info is None:
121
70
  print("Object not pushed to coop")
122
71
  return
123
- url = self.coop_info.get("url")
124
- webbrowser.open(url)
72
+ url = self.coop_info["url"]
73
+ import webbrowser
125
74
 
126
- def push(self, refresh: Optional[bool] = False) -> Dict[str, Any]:
127
- """
128
- Push the object to the Coop.
75
+ webbrowser.open(url)
129
76
 
130
- :param refresh: Whether to refresh the Coop entry for the object.
131
- :return: The Coop info dictionary.
132
- """
77
+ def push(self, refresh: bool = False) -> Dict[str, Any]:
133
78
  if self.coop_info is None or refresh:
134
79
  self.coop_info = self.object.push(description=self.description)
135
80
  print(
@@ -140,34 +85,13 @@ class ObjectEntry:
140
85
  f"Object {self.variable_name} already pushed to coop with info: {self._coop_info}"
141
86
  )
142
87
 
143
- def __eq__(self, other: "ObjectEntry") -> bool:
144
- """
145
- Check if two ObjectEntry instances are equal.
146
-
147
- :param other: The other ObjectEntry instance.
148
- :return: True if the two instances are equal, False otherwise.
149
- """
150
- # if the other item is not "ObjectEntry" type, return False
151
- if not isinstance(other, ObjectEntry):
152
- return False
153
-
154
- return (
155
- self.variable_name == other.variable_name
156
- and self.object == other.object
157
- and self.description == other.description
158
- and self.coop_info == other.coop_info
159
- and self.created_at == other.created_at
160
- and self.edsl_class_name == other.edsl_class_name
161
- )
162
-
163
88
 
164
89
  if __name__ == "__main__":
165
90
  from edsl import QuestionFreeText
166
- from edsl.study import ObjectEntry
167
91
 
168
92
  q = QuestionFreeText.example()
169
93
 
170
94
  oe = ObjectEntry("q", q, "This is a question")
171
95
  d = oe.to_dict()
172
96
  new_oe = ObjectEntry.from_dict(d)
173
- new_oe == oe
97
+ # print(oe.coop_info)
edsl/study/ProofOfWork.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import hashlib
2
2
  import time
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Optional, Any, Dict, List
4
4
 
5
5
 
6
6
  class ProofOfWork:
@@ -16,10 +16,7 @@ class ProofOfWork:
16
16
  self.input_data = input_data
17
17
 
18
18
  def to_dict(self) -> Dict[str, Any]:
19
- return {
20
- "input_data": self.input_data,
21
- "proof": self.proof,
22
- }
19
+ return {"input_data": self.input_data, "proof": self.proof}
23
20
 
24
21
  @classmethod
25
22
  def from_dict(cls, data: Dict[str, Any]) -> "ProofOfWork":
edsl/study/SnapShot.py CHANGED
@@ -1,11 +1,16 @@
1
+ from typing import Generator
1
2
  import inspect
2
- from typing import Generator, List, Optional
3
3
 
4
4
 
5
5
  class SnapShot:
6
- def __init__(self, namespace, exclude: Optional[List] = None):
6
+ def __init__(self, namespace, exclude=None):
7
7
  self.namespace = namespace
8
- self.exclude = exclude or []
8
+
9
+ if exclude is None:
10
+ self.exclude = []
11
+ else:
12
+ self.exclude = exclude
13
+
9
14
  self.edsl_objects = dict(self._get_edsl_objects(namespace=self.namespace))
10
15
  self.edsl_classes = dict(self._get_edsl_classes(namespace=self.namespace))
11
16
 
@@ -58,7 +63,6 @@ class SnapShot:
58
63
  from edsl.study.Study import Study
59
64
 
60
65
  for name, value in namespace.items():
61
- # TODO check this code logic (if there are other objects with to_dict method that are not from edsl)
62
66
  if (
63
67
  hasattr(value, "to_dict")
64
68
  and not inspect.isclass(value)
edsl/study/Study.py CHANGED
@@ -1,19 +1,23 @@
1
- import copy
2
- import inspect
3
- import json
4
1
  import os
5
2
  import platform
6
3
  import socket
4
+ import copy
5
+ import inspect
6
+ import json
7
+ from typing import Optional, List, Dict
7
8
  from datetime import datetime
8
- from typing import Dict, Optional, Union
9
+
10
+ # from edsl.Base import Base
9
11
  from edsl import Cache, set_session_cache, unset_session_cache
10
12
  from edsl.utilities.utilities import dict_hash
13
+
11
14
  from edsl.study.ObjectEntry import ObjectEntry
12
15
  from edsl.study.ProofOfWork import ProofOfWork
13
16
  from edsl.study.SnapShot import SnapShot
14
- from uuid import UUID
15
17
 
16
- # from edsl.Base import Base
18
+
19
+ class _StudyFrameMarker:
20
+ pass
17
21
 
18
22
 
19
23
  class Study:
@@ -54,7 +58,7 @@ class Study:
54
58
  proof_of_work=None,
55
59
  proof_of_work_difficulty: int = None,
56
60
  namespace: Optional[dict] = None,
57
- verbose: Optional[bool] = True,
61
+ verbose=True,
58
62
  ):
59
63
  """
60
64
  :param name: The name of the study.
@@ -465,22 +469,11 @@ class Study:
465
469
 
466
470
  def push(self, refresh=False) -> None:
467
471
  """Push the objects to coop."""
468
-
469
- from edsl import Coop
470
-
471
- coop = Coop()
472
- coop.create(self, description=self.description)
473
-
474
- @classmethod
475
- def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
476
- """Pull the object from coop."""
477
- from edsl.coop import Coop
478
-
479
- coop = Coop()
480
- return coop.get(uuid, url, "study")
472
+ for obj_entry in self.objects.values():
473
+ obj_entry.push(refresh=refresh)
481
474
 
482
475
  def __repr__(self):
483
- return f"""Study(name = "{self.name}", description = "{self.description}", objects = {self.objects}, cache = {self.cache}, filename = "{self.filename}", coop = {self.coop}, use_study_cache = {self.use_study_cache}, overwrite_on_change = {self.overwrite_on_change})"""
476
+ return f"""Study(name = {self.name}, description = {self.description}, objects = {self.objects}, cache = {self.cache}, filename = "{self.filename}", coop = {self.coop}, use_study_cache = {self.use_study_cache}, overwrite_on_change = {self.overwrite_on_change})"""
484
477
 
485
478
 
486
479
  if __name__ == "__main__":
edsl/study/__init__.py CHANGED
@@ -1,4 +1,2 @@
1
1
  from edsl.study.ObjectEntry import ObjectEntry
2
2
  from edsl.study.ProofOfWork import ProofOfWork
3
- from edsl.study.SnapShot import SnapShot
4
- from edsl.study.Study import Study
@@ -3,9 +3,9 @@
3
3
  from collections import UserDict, defaultdict
4
4
  from typing import Optional
5
5
 
6
- # from edsl.surveys.Memory import Memory
7
- # from edsl.prompts.Prompt import Prompt
8
- # from edsl.surveys.DAG import DAG
6
+ from edsl.surveys.Memory import Memory
7
+ from edsl.prompts.Prompt import Prompt
8
+ from edsl.surveys.DAG import DAG
9
9
 
10
10
 
11
11
  class MemoryPlan(UserDict):
@@ -61,8 +61,6 @@ class MemoryPlan(UserDict):
61
61
  :param answers: A dictionary of question names to answers.
62
62
 
63
63
  """
64
- from edsl.prompts.Prompt import Prompt
65
-
66
64
  self._check_valid_question_name(focal_question)
67
65
 
68
66
  if focal_question not in self:
@@ -123,7 +121,6 @@ class MemoryPlan(UserDict):
123
121
  self._check_valid_question_name(focal_question)
124
122
  self._check_valid_question_name(prior_question)
125
123
  self._check_order(focal_question, prior_question)
126
- from edsl.surveys.Memory import Memory
127
124
 
128
125
  if focal_question not in self:
129
126
  memory = Memory()
@@ -163,8 +160,6 @@ class MemoryPlan(UserDict):
163
160
  @classmethod
164
161
  def from_dict(cls, data) -> "MemoryPlan":
165
162
  """Deserialize a memory plan from a dictionary."""
166
- from edsl.surveys.Memory import Memory
167
-
168
163
  newdata = {}
169
164
  for question_name, memory in data["data"].items():
170
165
  newdata[question_name] = Memory.from_dict(memory)
@@ -187,15 +182,13 @@ class MemoryPlan(UserDict):
187
182
  return new_d
188
183
 
189
184
  @property
190
- def dag(self) -> "DAG":
185
+ def dag(self) -> DAG:
191
186
  """Return a directed acyclic graph of the memory plan.
192
187
 
193
188
  >>> mp = MemoryPlan.example()
194
189
  >>> mp.dag
195
190
  {1: {0}}
196
191
  """
197
- from edsl.surveys.DAG import DAG
198
-
199
192
  d = defaultdict(set)
200
193
  for focal_question, memory in self.items():
201
194
  for prior_question in memory:
edsl/surveys/Survey.py CHANGED
@@ -5,6 +5,9 @@ import re
5
5
 
6
6
  from typing import Any, Generator, Optional, Union, List, Literal, Callable
7
7
 
8
+ from rich import print
9
+ from rich.table import Table
10
+
8
11
  from edsl.exceptions import SurveyCreationError, SurveyHasNoRulesError
9
12
  from edsl.questions.QuestionBase import QuestionBase
10
13
  from edsl.surveys.base import RulePriority, EndOfSurvey
@@ -15,8 +18,8 @@ from edsl.Base import Base
15
18
  from edsl.surveys.SurveyExportMixin import SurveyExportMixin
16
19
  from edsl.surveys.descriptors import QuestionsDescriptor
17
20
  from edsl.surveys.MemoryPlan import MemoryPlan
18
-
19
21
  from edsl.surveys.DAG import DAG
22
+ from edsl.utilities import is_notebook
20
23
  from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
21
24
  from edsl.surveys.SurveyFlowVisualizationMixin import SurveyFlowVisualizationMixin
22
25
 
@@ -93,10 +96,6 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
93
96
  index = self.question_name_to_index[question_name]
94
97
  return self._questions[index]
95
98
 
96
- def question_names_to_questions(self) -> dict:
97
- """Return a dictionary mapping question names to question attributes."""
98
- return {q.question_name: q for q in self.questions}
99
-
100
99
  def get_question(self, question_name: str) -> QuestionBase:
101
100
  """Return the question object given the question name."""
102
101
  # import warnings
@@ -113,10 +112,6 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
113
112
  def parameters(self):
114
113
  return set.union(*[q.parameters for q in self.questions])
115
114
 
116
- @property
117
- def parameters_by_question(self):
118
- return {q.question_name: q.parameters for q in self.questions}
119
-
120
115
  @property
121
116
  def question_names(self) -> list[str]:
122
117
  """Return a list of question names in the survey.
@@ -560,12 +555,6 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
560
555
  job = Jobs(survey=self)
561
556
  return job.by(*args)
562
557
 
563
- def to_jobs(self):
564
- """Convert the survey to a Jobs object."""
565
- from edsl.jobs.Jobs import Jobs
566
-
567
- return Jobs(survey=self)
568
-
569
558
  def run(self, *args, **kwargs) -> "Results":
570
559
  """Turn the survey into a Job and runs it.
571
560
 
@@ -743,22 +732,6 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
743
732
  except IndexError:
744
733
  raise
745
734
 
746
- @property
747
- def piping_dag(self) -> DAG:
748
- d = {}
749
- for question_name, depenencies in self.parameters_by_question.items():
750
- if depenencies:
751
- question_index = self.question_name_to_index[question_name]
752
- for dependency in depenencies:
753
- if dependency not in self.question_name_to_index:
754
- pass
755
- else:
756
- dependency_index = self.question_name_to_index[dependency]
757
- if question_index not in d:
758
- d[question_index] = set()
759
- d[question_index].add(dependency_index)
760
- return d
761
-
762
735
  def dag(self, textify: bool = False) -> DAG:
763
736
  """Return the DAG of the survey, which reflects both skip-logic and memory.
764
737
 
@@ -772,12 +745,10 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
772
745
  """
773
746
  memory_dag = self.memory_plan.dag
774
747
  rule_dag = self.rule_collection.dag
775
- piping_dag = self.piping_dag
776
748
  if textify:
777
749
  memory_dag = DAG(self.textify(memory_dag))
778
750
  rule_dag = DAG(self.textify(rule_dag))
779
- piping_dag = DAG(self.textify(piping_dag))
780
- return memory_dag + rule_dag + piping_dag
751
+ return memory_dag + rule_dag
781
752
 
782
753
  ###################
783
754
  # DUNDER METHODS
@@ -960,8 +931,6 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
960
931
  │ └───────────────┴─────────────────┴───────────────┴──────────────────────────────────────────────┘ │
961
932
  └────────────────────────────────────────────────────────────────────────────────────────────────────┘
962
933
  """
963
- from rich.table import Table
964
-
965
934
  table = Table(show_header=True, header_style="bold magenta")
966
935
  table.add_column("Questions", style="dim")
967
936
 
@@ -1025,7 +994,7 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
1025
994
  return res
1026
995
 
1027
996
  @classmethod
1028
- def example(cls, params=False) -> Survey:
997
+ def example(cls) -> Survey:
1029
998
  """Return an example survey.
1030
999
 
1031
1000
  >>> s = Survey.example()
@@ -1049,20 +1018,12 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
1049
1018
  question_options=["**lack*** of killer bees in cafeteria", "other"],
1050
1019
  question_name="q2",
1051
1020
  )
1052
- if params:
1053
- q3 = QuestionMultipleChoice(
1054
- question_text="To the question '{{ q0.question_text}}', you said '{{ q0.answer }}'. Do you still feel this way?",
1055
- question_options=["yes", "no"],
1056
- question_name="q3",
1057
- )
1058
- s = cls(questions=[q0, q1, q2, q3])
1059
- return s
1060
1021
  s = cls(questions=[q0, q1, q2])
1061
1022
  s = s.add_rule(q0, "q0 == 'yes'", q2)
1062
1023
  return s
1063
1024
 
1064
1025
  def get_job(self, model=None, agent=None, **kwargs):
1065
- if model is None:
1026
+ if not model:
1066
1027
  from edsl import Model
1067
1028
 
1068
1029
  model = Model()
@@ -1,6 +1,8 @@
1
1
  """A mixin class for exporting surveys to different formats."""
2
2
 
3
+ from docx import Document
3
4
  from typing import Union, Optional
5
+ import black
4
6
 
5
7
 
6
8
  class SurveyExportMixin:
@@ -27,8 +29,6 @@ class SurveyExportMixin:
27
29
 
28
30
  def docx(self, filename=None) -> Union["Document", None]:
29
31
  """Generate a docx document for the survey."""
30
- from docx import Document
31
-
32
32
  doc = Document()
33
33
  doc.add_heading("EDSL Survey")
34
34
  doc.add_paragraph(f"\n")
@@ -83,8 +83,6 @@ class SurveyExportMixin:
83
83
  survey = Survey(questions=[q0, q1, q2])
84
84
  ...
85
85
  """
86
- import black
87
-
88
86
  header_lines = ["from edsl.surveys.Survey import Survey"]
89
87
  header_lines.append("from edsl import Question")
90
88
  lines = ["\n".join(header_lines)]
@@ -1,7 +1,10 @@
1
1
  """A mixin for visualizing the flow of a survey."""
2
2
 
3
- from edsl.surveys.base import RulePriority, EndOfSurvey
3
+ import pydot
4
4
  import tempfile
5
+ from IPython.display import Image
6
+ from edsl.utilities import is_notebook
7
+ from edsl.surveys.base import RulePriority, EndOfSurvey
5
8
 
6
9
 
7
10
  class SurveyFlowVisualizationMixin:
@@ -10,8 +13,6 @@ class SurveyFlowVisualizationMixin:
10
13
  def show_flow(self, filename: str = None):
11
14
  """Create an image showing the flow of users through the survey."""
12
15
  # Create a graph object
13
- import pydot
14
-
15
16
  graph = pydot.Dot(graph_type="digraph")
16
17
 
17
18
  # Add nodes for each question
@@ -100,11 +101,8 @@ class SurveyFlowVisualizationMixin:
100
101
  on Ubuntu.
101
102
  """
102
103
  )
103
- from edsl.utilities.utilities import is_notebook
104
104
 
105
105
  if is_notebook():
106
- from IPython.display import Image
107
-
108
106
  display(Image(tmp_file.name))
109
107
  else:
110
108
  import os