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.
- edsl/Base.py +18 -18
- edsl/__init__.py +24 -24
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +41 -77
- edsl/agents/AgentList.py +6 -35
- edsl/agents/Invigilator.py +1 -19
- edsl/agents/InvigilatorBase.py +10 -15
- edsl/agents/PromptConstructionMixin.py +100 -342
- edsl/agents/descriptors.py +1 -2
- edsl/config.py +1 -2
- edsl/conjure/InputData.py +8 -39
- edsl/coop/coop.py +150 -187
- edsl/coop/utils.py +75 -43
- edsl/data/Cache.py +5 -19
- edsl/data/SQLiteDict.py +3 -11
- edsl/jobs/Answers.py +1 -15
- edsl/jobs/Jobs.py +46 -90
- edsl/jobs/buckets/ModelBuckets.py +2 -4
- edsl/jobs/buckets/TokenBucket.py +2 -1
- edsl/jobs/interviews/Interview.py +9 -3
- edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +10 -15
- edsl/jobs/runners/JobsRunnerAsyncio.py +25 -21
- edsl/jobs/tasks/TaskHistory.py +3 -4
- edsl/language_models/LanguageModel.py +11 -5
- edsl/language_models/ModelList.py +3 -3
- edsl/language_models/repair.py +7 -8
- edsl/notebooks/Notebook.py +3 -40
- edsl/prompts/Prompt.py +19 -31
- edsl/questions/QuestionBase.py +13 -38
- edsl/questions/QuestionBudget.py +6 -5
- edsl/questions/QuestionCheckBox.py +3 -7
- edsl/questions/QuestionExtract.py +3 -5
- edsl/questions/QuestionFreeText.py +3 -3
- edsl/questions/QuestionFunctional.py +3 -0
- edsl/questions/QuestionList.py +4 -3
- edsl/questions/QuestionMultipleChoice.py +8 -16
- edsl/questions/QuestionNumerical.py +3 -4
- edsl/questions/QuestionRank.py +3 -5
- edsl/questions/__init__.py +3 -4
- edsl/questions/descriptors.py +2 -4
- edsl/questions/question_registry.py +31 -20
- edsl/questions/settings.py +1 -1
- edsl/results/Dataset.py +0 -31
- edsl/results/Result.py +74 -22
- edsl/results/Results.py +47 -97
- edsl/results/ResultsDBMixin.py +3 -7
- edsl/results/ResultsExportMixin.py +537 -22
- edsl/results/ResultsGGMixin.py +3 -3
- edsl/results/ResultsToolsMixin.py +5 -5
- edsl/scenarios/Scenario.py +6 -5
- edsl/scenarios/ScenarioList.py +11 -34
- edsl/scenarios/ScenarioListPdfMixin.py +1 -2
- edsl/scenarios/__init__.py +0 -1
- edsl/study/ObjectEntry.py +13 -89
- edsl/study/ProofOfWork.py +2 -5
- edsl/study/SnapShot.py +8 -4
- edsl/study/Study.py +14 -21
- edsl/study/__init__.py +0 -2
- edsl/surveys/MemoryPlan.py +4 -11
- edsl/surveys/Survey.py +7 -46
- edsl/surveys/SurveyExportMixin.py +2 -4
- edsl/surveys/SurveyFlowVisualizationMixin.py +4 -6
- edsl/tools/plotting.py +2 -4
- edsl/utilities/__init__.py +21 -21
- edsl/utilities/interface.py +45 -66
- edsl/utilities/utilities.py +13 -11
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/METADATA +10 -11
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/RECORD +72 -75
- edsl-0.1.29.dev1.dist-info/entry_points.txt +3 -0
- edsl/base/Base.py +0 -289
- edsl/results/DatasetExportMixin.py +0 -493
- edsl/scenarios/FileStore.py +0 -140
- edsl/scenarios/ScenarioListExportMixin.py +0 -32
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/LICENSE +0 -0
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/WHEEL +0 -0
edsl/scenarios/ScenarioList.py
CHANGED
@@ -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
|
-
|
20
|
-
pass
|
21
|
+
from edsl.utilities import is_valid_variable_name
|
21
22
|
|
23
|
+
from edsl.results.ResultsExportMixin import ResultsExportMixin
|
22
24
|
|
23
|
-
|
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 +
|
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
|
-
|
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
|
edsl/scenarios/__init__.py
CHANGED
edsl/study/ObjectEntry.py
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
import time
|
2
|
-
import
|
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(
|
37
|
-
""
|
38
|
-
|
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
|
124
|
-
webbrowser
|
72
|
+
url = self.coop_info["url"]
|
73
|
+
import webbrowser
|
125
74
|
|
126
|
-
|
127
|
-
"""
|
128
|
-
Push the object to the Coop.
|
75
|
+
webbrowser.open(url)
|
129
76
|
|
130
|
-
|
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
|
-
|
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
|
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
|
6
|
+
def __init__(self, namespace, exclude=None):
|
7
7
|
self.namespace = namespace
|
8
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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 =
|
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
edsl/surveys/MemoryPlan.py
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
from collections import UserDict, defaultdict
|
4
4
|
from typing import Optional
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
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) ->
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|