edsl 0.1.30__py3-none-any.whl → 0.1.30.dev2__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/__version__.py +1 -1
- edsl/agents/Agent.py +6 -8
- edsl/agents/AgentList.py +19 -9
- edsl/agents/Invigilator.py +1 -1
- edsl/conversation/car_buying.py +1 -1
- edsl/data/Cache.py +14 -13
- edsl/data/CacheEntry.py +7 -6
- edsl/data_transfer_models.py +1 -1
- edsl/jobs/Jobs.py +2 -10
- edsl/jobs/buckets/ModelBuckets.py +1 -1
- edsl/jobs/buckets/TokenBucket.py +4 -15
- edsl/jobs/interviews/Interview.py +73 -98
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +19 -9
- edsl/jobs/runners/JobsRunnerAsyncio.py +0 -2
- edsl/jobs/tasks/QuestionTaskCreator.py +7 -2
- edsl/language_models/LanguageModel.py +1 -8
- edsl/notebooks/Notebook.py +9 -9
- edsl/questions/QuestionFreeText.py +2 -4
- edsl/questions/QuestionFunctional.py +2 -34
- edsl/questions/QuestionMultipleChoice.py +8 -57
- edsl/questions/descriptors.py +2 -42
- edsl/results/DatasetExportMixin.py +5 -84
- edsl/results/Result.py +5 -53
- edsl/results/Results.py +30 -70
- edsl/scenarios/FileStore.py +26 -42
- edsl/scenarios/Scenario.py +19 -12
- edsl/scenarios/ScenarioList.py +6 -8
- edsl/study/Study.py +7 -5
- edsl/surveys/Survey.py +12 -44
- {edsl-0.1.30.dist-info → edsl-0.1.30.dev2.dist-info}/METADATA +1 -1
- {edsl-0.1.30.dist-info → edsl-0.1.30.dev2.dist-info}/RECORD +33 -33
- {edsl-0.1.30.dist-info → edsl-0.1.30.dev2.dist-info}/WHEEL +1 -1
- {edsl-0.1.30.dist-info → edsl-0.1.30.dev2.dist-info}/LICENSE +0 -0
edsl/results/Results.py
CHANGED
@@ -290,8 +290,7 @@ class Results(UserList, Mixins, Base):
|
|
290
290
|
),
|
291
291
|
)
|
292
292
|
except Exception as e:
|
293
|
-
|
294
|
-
# breakpoint()
|
293
|
+
breakpoint()
|
295
294
|
return results
|
296
295
|
|
297
296
|
######################
|
@@ -604,38 +603,6 @@ class Results(UserList, Mixins, Base):
|
|
604
603
|
self = self.add_column(key, values)
|
605
604
|
return self
|
606
605
|
|
607
|
-
@staticmethod
|
608
|
-
def _create_evaluator(
|
609
|
-
result: Result, functions_dict: Optional[dict] = None
|
610
|
-
) -> EvalWithCompoundTypes:
|
611
|
-
"""Create an evaluator for the expression.
|
612
|
-
|
613
|
-
>>> from unittest.mock import Mock
|
614
|
-
>>> result = Mock()
|
615
|
-
>>> result.combined_dict = {'how_feeling': 'OK'}
|
616
|
-
|
617
|
-
>>> evaluator = Results._create_evaluator(result = result, functions_dict = {})
|
618
|
-
>>> evaluator.eval("how_feeling == 'OK'")
|
619
|
-
True
|
620
|
-
|
621
|
-
>>> result.combined_dict = {'answer': {'how_feeling': 'OK'}}
|
622
|
-
>>> evaluator = Results._create_evaluator(result = result, functions_dict = {})
|
623
|
-
>>> evaluator.eval("answer.how_feeling== 'OK'")
|
624
|
-
True
|
625
|
-
|
626
|
-
Note that you need to refer to the answer dictionary in the expression.
|
627
|
-
|
628
|
-
>>> evaluator.eval("how_feeling== 'OK'")
|
629
|
-
Traceback (most recent call last):
|
630
|
-
...
|
631
|
-
simpleeval.NameNotDefined: 'how_feeling' is not defined for expression 'how_feeling== 'OK''
|
632
|
-
"""
|
633
|
-
if functions_dict is None:
|
634
|
-
functions_dict = {}
|
635
|
-
return EvalWithCompoundTypes(
|
636
|
-
names=result.combined_dict, functions=functions_dict
|
637
|
-
)
|
638
|
-
|
639
606
|
def mutate(
|
640
607
|
self, new_var_string: str, functions_dict: Optional[dict] = None
|
641
608
|
) -> Results:
|
@@ -668,8 +635,13 @@ class Results(UserList, Mixins, Base):
|
|
668
635
|
# create the evaluator
|
669
636
|
functions_dict = functions_dict or {}
|
670
637
|
|
638
|
+
def create_evaluator(result: Result) -> EvalWithCompoundTypes:
|
639
|
+
return EvalWithCompoundTypes(
|
640
|
+
names=result.combined_dict, functions=functions_dict
|
641
|
+
)
|
642
|
+
|
671
643
|
def new_result(old_result: "Result", var_name: str) -> "Result":
|
672
|
-
evaluator =
|
644
|
+
evaluator = create_evaluator(old_result)
|
673
645
|
value = evaluator.eval(expression)
|
674
646
|
new_result = old_result.copy()
|
675
647
|
new_result["answer"][var_name] = value
|
@@ -769,9 +741,6 @@ class Results(UserList, Mixins, Base):
|
|
769
741
|
>>> results = Results.example()
|
770
742
|
>>> results.select('how_feeling')
|
771
743
|
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
|
772
|
-
|
773
|
-
>>> results.select('how_feeling', 'model', 'how_feeling')
|
774
|
-
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'model.model': ['gpt-4-1106-preview', 'gpt-4-1106-preview', 'gpt-4-1106-preview', 'gpt-4-1106-preview']}, {'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
|
775
744
|
"""
|
776
745
|
|
777
746
|
if len(self) == 0:
|
@@ -829,19 +798,10 @@ class Results(UserList, Mixins, Base):
|
|
829
798
|
# Return the index of this key in the list_of_keys
|
830
799
|
return items_in_order.index(single_key)
|
831
800
|
|
832
|
-
|
801
|
+
sorted(new_data, key=sort_by_key_order)
|
833
802
|
from edsl.results.Dataset import Dataset
|
834
803
|
|
835
|
-
|
836
|
-
|
837
|
-
# WORKS but slow
|
838
|
-
for key in items_in_order:
|
839
|
-
for d in new_data:
|
840
|
-
if key in d:
|
841
|
-
sorted_new_data.append(d)
|
842
|
-
break
|
843
|
-
|
844
|
-
return Dataset(sorted_new_data)
|
804
|
+
return Dataset(new_data)
|
845
805
|
|
846
806
|
def sort_by(self, *columns: str, reverse: bool = False) -> Results:
|
847
807
|
import warnings
|
@@ -956,29 +916,29 @@ class Results(UserList, Mixins, Base):
|
|
956
916
|
"You must use '==' instead of '=' in the filter expression."
|
957
917
|
)
|
958
918
|
|
919
|
+
def create_evaluator(result):
|
920
|
+
"""Create an evaluator for the given result.
|
921
|
+
The 'combined_dict' is a mapping of all values for that Result object.
|
922
|
+
"""
|
923
|
+
return EvalWithCompoundTypes(names=result.combined_dict)
|
924
|
+
|
959
925
|
try:
|
960
926
|
# iterates through all the results and evaluates the expression
|
961
|
-
new_data = [
|
962
|
-
|
963
|
-
|
964
|
-
result.
|
965
|
-
|
966
|
-
new_data.append(result)
|
967
|
-
|
968
|
-
except ValueError as e:
|
969
|
-
raise ResultsFilterError(
|
970
|
-
f"Error in filter. Exception:{e}",
|
971
|
-
f"The expression you provided was: {expression}",
|
972
|
-
"See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.",
|
973
|
-
)
|
927
|
+
new_data = [
|
928
|
+
result
|
929
|
+
for result in self.data
|
930
|
+
if create_evaluator(result).eval(expression)
|
931
|
+
]
|
974
932
|
except Exception as e:
|
975
933
|
raise ResultsFilterError(
|
976
|
-
f"""Error in filter. Exception:{e}.
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
934
|
+
f"""Error in filter. Exception:{e}.
|
935
|
+
The expression you provided was: {expression}.
|
936
|
+
Please make sure that the expression is a valid Python expression that evaluates to a boolean.
|
937
|
+
For example, 'how_feeling == "Great"' is a valid expression, as is 'how_feeling in ["Great", "Terrible"]'.
|
938
|
+
However, 'how_feeling = "Great"' is not a valid expression.
|
939
|
+
|
940
|
+
See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.
|
941
|
+
"""
|
982
942
|
)
|
983
943
|
|
984
944
|
if len(new_data) == 0:
|
@@ -989,7 +949,7 @@ class Results(UserList, Mixins, Base):
|
|
989
949
|
return Results(survey=self.survey, data=new_data, created_columns=None)
|
990
950
|
|
991
951
|
@classmethod
|
992
|
-
def example(cls, debug: bool = False
|
952
|
+
def example(cls, debug: bool = False) -> Results:
|
993
953
|
"""Return an example `Results` object.
|
994
954
|
|
995
955
|
Example usage:
|
@@ -1002,7 +962,7 @@ class Results(UserList, Mixins, Base):
|
|
1002
962
|
from edsl.data.Cache import Cache
|
1003
963
|
|
1004
964
|
c = Cache()
|
1005
|
-
job = Jobs.example(
|
965
|
+
job = Jobs.example()
|
1006
966
|
results = job.run(cache=c, debug=debug)
|
1007
967
|
return results
|
1008
968
|
|
edsl/scenarios/FileStore.py
CHANGED
@@ -31,7 +31,7 @@ class FileStore(Scenario):
|
|
31
31
|
@classmethod
|
32
32
|
def from_dict(cls, d):
|
33
33
|
return cls(d["filename"], d["binary"], d["suffix"], d["base64_string"])
|
34
|
-
|
34
|
+
|
35
35
|
def __repr__(self):
|
36
36
|
return f"FileStore(filename='{self.filename}', binary='{self.binary}', 'suffix'={self.suffix})"
|
37
37
|
|
@@ -89,6 +89,7 @@ class FileStore(Scenario):
|
|
89
89
|
# Create a StringIO object for text data
|
90
90
|
return io.StringIO(text_data)
|
91
91
|
|
92
|
+
|
92
93
|
def to_tempfile(self, suffix=None):
|
93
94
|
if suffix is None:
|
94
95
|
suffix = self.suffix
|
@@ -100,14 +101,14 @@ class FileStore(Scenario):
|
|
100
101
|
file_like_object = self.base64_to_text_file(self["base64_string"])
|
101
102
|
|
102
103
|
# Create a named temporary file
|
103
|
-
mode =
|
104
|
+
mode = 'wb' if self.binary else 'w'
|
104
105
|
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, mode=mode)
|
105
|
-
|
106
|
+
|
106
107
|
if self.binary:
|
107
108
|
temp_file.write(file_like_object.read())
|
108
109
|
else:
|
109
110
|
temp_file.write(file_like_object.read())
|
110
|
-
|
111
|
+
|
111
112
|
temp_file.close()
|
112
113
|
|
113
114
|
return temp_file.name
|
@@ -132,19 +133,16 @@ class CSVFileStore(FileStore):
|
|
132
133
|
@classmethod
|
133
134
|
def example(cls):
|
134
135
|
from edsl.results.Results import Results
|
135
|
-
|
136
136
|
r = Results.example()
|
137
137
|
import tempfile
|
138
|
-
|
139
138
|
with tempfile.NamedTemporaryFile(suffix=".csv", delete=False) as f:
|
140
139
|
r.to_csv(filename=f.name)
|
141
140
|
return cls(f.name)
|
142
|
-
|
141
|
+
|
143
142
|
def view(self):
|
144
143
|
import pandas as pd
|
145
|
-
|
146
144
|
return pd.read_csv(self.to_tempfile())
|
147
|
-
|
145
|
+
|
148
146
|
|
149
147
|
class PDFFileStore(FileStore):
|
150
148
|
def __init__(self, filename):
|
@@ -155,27 +153,24 @@ class PDFFileStore(FileStore):
|
|
155
153
|
print(f"PDF path: {pdf_path}") # Print the path to ensure it exists
|
156
154
|
import os
|
157
155
|
import subprocess
|
158
|
-
|
159
156
|
if os.path.exists(pdf_path):
|
160
157
|
try:
|
161
|
-
if os.name ==
|
158
|
+
if os.name == 'posix':
|
162
159
|
# for cool kids
|
163
|
-
subprocess.run([
|
164
|
-
elif os.name ==
|
160
|
+
subprocess.run(['open', pdf_path], check=True) # macOS
|
161
|
+
elif os.name == 'nt':
|
165
162
|
os.startfile(pdf_path) # Windows
|
166
163
|
else:
|
167
|
-
subprocess.run([
|
164
|
+
subprocess.run(['xdg-open', pdf_path], check=True) # Linux
|
168
165
|
except Exception as e:
|
169
166
|
print(f"Error opening PDF: {e}")
|
170
167
|
else:
|
171
168
|
print("PDF file was not created successfully.")
|
172
169
|
|
173
170
|
@classmethod
|
174
|
-
def example(cls):
|
171
|
+
def example(cls):
|
175
172
|
import textwrap
|
176
|
-
|
177
|
-
pdf_string = textwrap.dedent(
|
178
|
-
"""\
|
173
|
+
pdf_string = textwrap.dedent("""\
|
179
174
|
%PDF-1.4
|
180
175
|
1 0 obj
|
181
176
|
<< /Type /Catalog /Pages 2 0 R >>
|
@@ -215,15 +210,12 @@ class PDFFileStore(FileStore):
|
|
215
210
|
<< /Size 7 /Root 1 0 R >>
|
216
211
|
startxref
|
217
212
|
318
|
218
|
-
%%EOF"""
|
219
|
-
)
|
213
|
+
%%EOF""")
|
220
214
|
import tempfile
|
221
|
-
|
222
215
|
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as f:
|
223
216
|
f.write(pdf_string.encode())
|
224
217
|
return cls(f.name)
|
225
218
|
|
226
|
-
|
227
219
|
class PNGFileStore(FileStore):
|
228
220
|
def __init__(self, filename):
|
229
221
|
super().__init__(filename, suffix=".png")
|
@@ -231,25 +223,19 @@ class PNGFileStore(FileStore):
|
|
231
223
|
@classmethod
|
232
224
|
def example(cls):
|
233
225
|
import textwrap
|
234
|
-
|
235
|
-
png_string = textwrap.dedent(
|
236
|
-
"""\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\x00\x00\x00\x01\x00\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\xd7c\x00\x01"""
|
237
|
-
)
|
226
|
+
png_string = textwrap.dedent("""\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\x00\x00\x00\x01\x00\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\xd7c\x00\x01""")
|
238
227
|
import tempfile
|
239
|
-
|
240
228
|
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
|
241
229
|
f.write(png_string.encode())
|
242
230
|
return cls(f.name)
|
243
|
-
|
231
|
+
|
244
232
|
def view(self):
|
245
233
|
import matplotlib.pyplot as plt
|
246
234
|
import matplotlib.image as mpimg
|
247
|
-
|
248
235
|
img = mpimg.imread(self.to_tempfile())
|
249
236
|
plt.imshow(img)
|
250
237
|
plt.show()
|
251
238
|
|
252
|
-
|
253
239
|
class SQLiteFileStore(FileStore):
|
254
240
|
def __init__(self, filename):
|
255
241
|
super().__init__(filename, suffix=".sqlite")
|
@@ -258,20 +244,18 @@ class SQLiteFileStore(FileStore):
|
|
258
244
|
def example(cls):
|
259
245
|
import sqlite3
|
260
246
|
import tempfile
|
261
|
-
|
262
247
|
with tempfile.NamedTemporaryFile(suffix=".sqlite", delete=False) as f:
|
263
248
|
conn = sqlite3.connect(f.name)
|
264
249
|
c = conn.cursor()
|
265
|
-
c.execute(
|
250
|
+
c.execute('''CREATE TABLE stocks (date text)''')
|
266
251
|
conn.commit()
|
267
252
|
|
268
253
|
def view(self):
|
269
254
|
import subprocess
|
270
255
|
import os
|
271
|
-
|
272
256
|
sqlite_path = self.to_tempfile()
|
273
257
|
os.system(f"sqlite3 {sqlite_path}")
|
274
|
-
|
258
|
+
|
275
259
|
|
276
260
|
if __name__ == "__main__":
|
277
261
|
# file_path = "../conjure/examples/Ex11-2.sav"
|
@@ -279,21 +263,21 @@ if __name__ == "__main__":
|
|
279
263
|
# info = fs.push()
|
280
264
|
# print(info)
|
281
265
|
|
282
|
-
#
|
283
|
-
#
|
266
|
+
#fs = CSVFileStore.example()
|
267
|
+
#fs.to_tempfile()
|
284
268
|
# print(fs.view())
|
285
269
|
|
286
|
-
#
|
287
|
-
#
|
270
|
+
#fs = PDFFileStore.example()
|
271
|
+
#fs.view()
|
288
272
|
|
289
|
-
#
|
290
|
-
#
|
273
|
+
#fs = PDFFileStore("paper.pdf")
|
274
|
+
#fs.view()
|
291
275
|
# from edsl import Conjure
|
292
276
|
|
293
277
|
fs = PNGFileStore("robot.png")
|
294
278
|
fs.view()
|
295
279
|
|
296
280
|
# c = Conjure(datafile_name=fs.to_tempfile())
|
297
|
-
#
|
281
|
+
#f = PDFFileStore("paper.pdf")
|
298
282
|
# print(f.to_tempfile())
|
299
|
-
#
|
283
|
+
#f.push()
|
edsl/scenarios/Scenario.py
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
"""A Scenario is a dictionary with a key/value to parameterize a question."""
|
2
2
|
|
3
|
-
|
3
|
+
import time
|
4
4
|
import copy
|
5
|
+
from collections import UserDict
|
6
|
+
from typing import Union, List, Optional, Generator
|
5
7
|
import base64
|
6
8
|
import hashlib
|
7
9
|
import os
|
8
|
-
|
9
|
-
from typing import Union, List, Optional, Generator
|
10
|
-
from uuid import uuid4
|
10
|
+
|
11
11
|
from edsl.Base import Base
|
12
12
|
from edsl.scenarios.ScenarioImageMixin import ScenarioImageMixin
|
13
13
|
from edsl.scenarios.ScenarioHtmlMixin import ScenarioHtmlMixin
|
14
|
-
|
14
|
+
|
15
|
+
from edsl.utilities.decorators import (
|
16
|
+
add_edsl_version,
|
17
|
+
remove_edsl_version,
|
18
|
+
)
|
15
19
|
|
16
20
|
|
17
21
|
class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
@@ -24,7 +28,9 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
24
28
|
|
25
29
|
:param data: A dictionary of keys/values for parameterizing questions.
|
26
30
|
"""
|
27
|
-
|
31
|
+
if data is None:
|
32
|
+
data = {}
|
33
|
+
self.data = data
|
28
34
|
self.name = name
|
29
35
|
|
30
36
|
def replicate(self, n: int) -> "ScenarioList":
|
@@ -409,16 +415,17 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
409
415
|
return table
|
410
416
|
|
411
417
|
@classmethod
|
412
|
-
def example(cls
|
413
|
-
"""
|
414
|
-
|
418
|
+
def example(cls) -> "Scenario":
|
419
|
+
"""Return an example scenario.
|
420
|
+
|
421
|
+
Example:
|
415
422
|
|
416
|
-
|
423
|
+
>>> Scenario.example()
|
424
|
+
Scenario({'persona': 'A reseacher studying whether LLMs can be used to generate surveys.'})
|
417
425
|
"""
|
418
|
-
addition = "" if not randomize else str(uuid4())
|
419
426
|
return cls(
|
420
427
|
{
|
421
|
-
"persona":
|
428
|
+
"persona": "A reseacher studying whether LLMs can be used to generate surveys."
|
422
429
|
}
|
423
430
|
)
|
424
431
|
|
edsl/scenarios/ScenarioList.py
CHANGED
@@ -5,8 +5,10 @@ import csv
|
|
5
5
|
import random
|
6
6
|
from collections import UserList, Counter
|
7
7
|
from collections.abc import Iterable
|
8
|
-
from simpleeval import EvalWithCompoundTypes
|
9
8
|
from typing import Any, Optional, Union, List
|
9
|
+
|
10
|
+
from simpleeval import EvalWithCompoundTypes
|
11
|
+
|
10
12
|
from edsl.Base import Base
|
11
13
|
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
12
14
|
from edsl.scenarios.Scenario import Scenario
|
@@ -440,13 +442,9 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
440
442
|
return lines
|
441
443
|
|
442
444
|
@classmethod
|
443
|
-
def example(cls
|
444
|
-
"""
|
445
|
-
|
446
|
-
|
447
|
-
:params randomize: If True, use Scenario's randomize method to randomize the values.
|
448
|
-
"""
|
449
|
-
return cls([Scenario.example(randomize), Scenario.example(randomize)])
|
445
|
+
def example(cls) -> ScenarioList:
|
446
|
+
"""Return an example of the `ScenarioList`."""
|
447
|
+
return cls([Scenario.example(), Scenario.example()])
|
450
448
|
|
451
449
|
def rich_print(self) -> None:
|
452
450
|
"""Display an object as a table."""
|
edsl/study/Study.py
CHANGED
@@ -6,12 +6,14 @@ import platform
|
|
6
6
|
import socket
|
7
7
|
from datetime import datetime
|
8
8
|
from typing import Dict, Optional, Union
|
9
|
-
from uuid import UUID, uuid4
|
10
9
|
from edsl import Cache, set_session_cache, unset_session_cache
|
11
10
|
from edsl.utilities.utilities import dict_hash
|
12
11
|
from edsl.study.ObjectEntry import ObjectEntry
|
13
12
|
from edsl.study.ProofOfWork import ProofOfWork
|
14
13
|
from edsl.study.SnapShot import SnapShot
|
14
|
+
from uuid import UUID
|
15
|
+
|
16
|
+
# from edsl.Base import Base
|
15
17
|
|
16
18
|
|
17
19
|
class Study:
|
@@ -400,14 +402,14 @@ class Study:
|
|
400
402
|
return diff
|
401
403
|
|
402
404
|
@classmethod
|
403
|
-
def example(cls, verbose=False
|
405
|
+
def example(cls, verbose=False):
|
404
406
|
import tempfile
|
405
407
|
|
406
408
|
study_file = tempfile.NamedTemporaryFile()
|
407
409
|
with cls(filename=study_file.name, verbose=verbose) as study:
|
408
410
|
from edsl import QuestionFreeText
|
409
411
|
|
410
|
-
q = QuestionFreeText.example(
|
412
|
+
q = QuestionFreeText.example()
|
411
413
|
return study
|
412
414
|
|
413
415
|
@classmethod
|
@@ -461,13 +463,13 @@ class Study:
|
|
461
463
|
else:
|
462
464
|
self.objects[oe.hash] = oe
|
463
465
|
|
464
|
-
def push(self) ->
|
466
|
+
def push(self, refresh=False) -> None:
|
465
467
|
"""Push the objects to coop."""
|
466
468
|
|
467
469
|
from edsl import Coop
|
468
470
|
|
469
471
|
coop = Coop()
|
470
|
-
|
472
|
+
coop.create(self, description=self.description)
|
471
473
|
|
472
474
|
@classmethod
|
473
475
|
def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
|
edsl/surveys/Survey.py
CHANGED
@@ -2,20 +2,23 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
import re
|
5
|
+
|
5
6
|
from typing import Any, Generator, Optional, Union, List, Literal, Callable
|
6
|
-
|
7
|
-
from edsl.Base import Base
|
7
|
+
|
8
8
|
from edsl.exceptions import SurveyCreationError, SurveyHasNoRulesError
|
9
9
|
from edsl.questions.QuestionBase import QuestionBase
|
10
10
|
from edsl.surveys.base import RulePriority, EndOfSurvey
|
11
|
-
from edsl.surveys.DAG import DAG
|
12
|
-
from edsl.surveys.descriptors import QuestionsDescriptor
|
13
|
-
from edsl.surveys.MemoryPlan import MemoryPlan
|
14
11
|
from edsl.surveys.Rule import Rule
|
15
12
|
from edsl.surveys.RuleCollection import RuleCollection
|
13
|
+
|
14
|
+
from edsl.Base import Base
|
16
15
|
from edsl.surveys.SurveyExportMixin import SurveyExportMixin
|
17
|
-
from edsl.surveys.
|
16
|
+
from edsl.surveys.descriptors import QuestionsDescriptor
|
17
|
+
from edsl.surveys.MemoryPlan import MemoryPlan
|
18
|
+
|
19
|
+
from edsl.surveys.DAG import DAG
|
18
20
|
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
21
|
+
from edsl.surveys.SurveyFlowVisualizationMixin import SurveyFlowVisualizationMixin
|
19
22
|
|
20
23
|
|
21
24
|
class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
|
@@ -106,39 +109,6 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
|
|
106
109
|
|
107
110
|
return dict_hash(self._to_dict())
|
108
111
|
|
109
|
-
def __add__(self, other: Survey) -> Survey:
|
110
|
-
"""Combine two surveys.
|
111
|
-
|
112
|
-
:param other: The other survey to combine with this one.
|
113
|
-
>>> s1 = Survey.example()
|
114
|
-
>>> from edsl import QuestionFreeText
|
115
|
-
>>> s2 = Survey([QuestionFreeText(question_text="What is your name?", question_name="yo")])
|
116
|
-
>>> s3 = s1 + s2
|
117
|
-
Traceback (most recent call last):
|
118
|
-
...
|
119
|
-
ValueError: ('Cannot combine two surveys with non-default rules.', "Please use the 'clear_non_default_rules' method to remove non-default rules from the survey.")
|
120
|
-
>>> s3 = s1.clear_non_default_rules() + s2
|
121
|
-
>>> len(s3.questions)
|
122
|
-
4
|
123
|
-
|
124
|
-
"""
|
125
|
-
if (
|
126
|
-
len(self.rule_collection.non_default_rules) > 0
|
127
|
-
or len(other.rule_collection.non_default_rules) > 0
|
128
|
-
):
|
129
|
-
raise ValueError(
|
130
|
-
"Cannot combine two surveys with non-default rules.",
|
131
|
-
"Please use the 'clear_non_default_rules' method to remove non-default rules from the survey.",
|
132
|
-
)
|
133
|
-
|
134
|
-
return Survey(questions=self.questions + other.questions)
|
135
|
-
|
136
|
-
def clear_non_default_rules(self) -> Survey:
|
137
|
-
s = Survey()
|
138
|
-
for question in self.questions:
|
139
|
-
s.add_question(question)
|
140
|
-
return s
|
141
|
-
|
142
112
|
@property
|
143
113
|
def parameters(self):
|
144
114
|
return set.union(*[q.parameters for q in self.questions])
|
@@ -1055,7 +1025,7 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
|
|
1055
1025
|
return res
|
1056
1026
|
|
1057
1027
|
@classmethod
|
1058
|
-
def example(cls, params
|
1028
|
+
def example(cls, params=False) -> Survey:
|
1059
1029
|
"""Return an example survey.
|
1060
1030
|
|
1061
1031
|
>>> s = Survey.example()
|
@@ -1064,9 +1034,8 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
|
|
1064
1034
|
"""
|
1065
1035
|
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
1066
1036
|
|
1067
|
-
addition = "" if not randomize else str(uuid4())
|
1068
1037
|
q0 = QuestionMultipleChoice(
|
1069
|
-
question_text=
|
1038
|
+
question_text="Do you like school?",
|
1070
1039
|
question_options=["yes", "no"],
|
1071
1040
|
question_name="q0",
|
1072
1041
|
)
|
@@ -1184,5 +1153,4 @@ def main():
|
|
1184
1153
|
if __name__ == "__main__":
|
1185
1154
|
import doctest
|
1186
1155
|
|
1187
|
-
|
1188
|
-
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
1156
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.SKIP)
|