edsl 0.1.29.dev6__py3-none-any.whl → 0.1.30__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edsl/Base.py +6 -3
- edsl/__init__.py +23 -23
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +43 -40
- edsl/agents/AgentList.py +23 -22
- edsl/agents/Invigilator.py +19 -2
- edsl/agents/descriptors.py +2 -1
- edsl/base/Base.py +289 -0
- edsl/config.py +2 -1
- edsl/conversation/car_buying.py +1 -1
- edsl/coop/utils.py +28 -1
- edsl/data/Cache.py +41 -18
- edsl/data/CacheEntry.py +6 -7
- edsl/data/SQLiteDict.py +11 -3
- edsl/data_transfer_models.py +4 -0
- edsl/jobs/Answers.py +15 -1
- edsl/jobs/Jobs.py +86 -33
- edsl/jobs/buckets/ModelBuckets.py +14 -2
- edsl/jobs/buckets/TokenBucket.py +32 -5
- edsl/jobs/interviews/Interview.py +99 -79
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +18 -24
- edsl/jobs/runners/JobsRunnerAsyncio.py +16 -16
- edsl/jobs/tasks/QuestionTaskCreator.py +10 -6
- edsl/jobs/tasks/TaskHistory.py +4 -3
- edsl/language_models/LanguageModel.py +17 -17
- edsl/language_models/ModelList.py +1 -1
- edsl/language_models/repair.py +8 -7
- edsl/notebooks/Notebook.py +16 -10
- edsl/questions/QuestionBase.py +6 -2
- edsl/questions/QuestionBudget.py +5 -6
- edsl/questions/QuestionCheckBox.py +7 -3
- edsl/questions/QuestionExtract.py +5 -3
- edsl/questions/QuestionFreeText.py +7 -5
- edsl/questions/QuestionFunctional.py +34 -5
- edsl/questions/QuestionList.py +3 -4
- edsl/questions/QuestionMultipleChoice.py +68 -12
- edsl/questions/QuestionNumerical.py +4 -3
- edsl/questions/QuestionRank.py +5 -3
- edsl/questions/__init__.py +4 -3
- edsl/questions/descriptors.py +46 -4
- edsl/results/DatasetExportMixin.py +570 -0
- edsl/results/Result.py +66 -70
- edsl/results/Results.py +160 -68
- edsl/results/ResultsDBMixin.py +7 -3
- edsl/results/ResultsExportMixin.py +22 -537
- edsl/results/ResultsGGMixin.py +3 -3
- edsl/results/ResultsToolsMixin.py +1 -4
- edsl/scenarios/FileStore.py +299 -0
- edsl/scenarios/Scenario.py +16 -24
- edsl/scenarios/ScenarioList.py +25 -14
- edsl/scenarios/ScenarioListExportMixin.py +32 -0
- edsl/scenarios/ScenarioListPdfMixin.py +2 -1
- edsl/scenarios/__init__.py +1 -0
- edsl/study/Study.py +5 -7
- edsl/surveys/MemoryPlan.py +11 -4
- edsl/surveys/Survey.py +52 -15
- edsl/surveys/SurveyExportMixin.py +4 -2
- edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
- edsl/utilities/__init__.py +21 -21
- edsl/utilities/interface.py +66 -45
- edsl/utilities/utilities.py +11 -13
- {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/METADATA +1 -1
- {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/RECORD +65 -61
- {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/WHEEL +1 -1
- {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/LICENSE +0 -0
@@ -0,0 +1,299 @@
|
|
1
|
+
from edsl import Scenario
|
2
|
+
import base64
|
3
|
+
import io
|
4
|
+
import tempfile
|
5
|
+
from typing import Optional
|
6
|
+
|
7
|
+
|
8
|
+
class FileStore(Scenario):
|
9
|
+
def __init__(
|
10
|
+
self,
|
11
|
+
filename: str,
|
12
|
+
binary: Optional[bool] = None,
|
13
|
+
suffix: Optional[str] = None,
|
14
|
+
base64_string: Optional[str] = None,
|
15
|
+
):
|
16
|
+
self.filename = filename
|
17
|
+
self.suffix = suffix or "." + filename.split(".")[-1]
|
18
|
+
self.binary = binary or False
|
19
|
+
self.base64_string = base64_string or self.encode_file_to_base64_string(
|
20
|
+
filename
|
21
|
+
)
|
22
|
+
super().__init__(
|
23
|
+
{
|
24
|
+
"filename": self.filename,
|
25
|
+
"base64_string": self.base64_string,
|
26
|
+
"binary": self.binary,
|
27
|
+
"suffix": self.suffix,
|
28
|
+
}
|
29
|
+
)
|
30
|
+
|
31
|
+
@classmethod
|
32
|
+
def from_dict(cls, d):
|
33
|
+
return cls(d["filename"], d["binary"], d["suffix"], d["base64_string"])
|
34
|
+
|
35
|
+
def __repr__(self):
|
36
|
+
return f"FileStore(filename='{self.filename}', binary='{self.binary}', 'suffix'={self.suffix})"
|
37
|
+
|
38
|
+
def encode_file_to_base64_string(self, file_path):
|
39
|
+
try:
|
40
|
+
# Attempt to open the file in text mode
|
41
|
+
with open(file_path, "r") as text_file:
|
42
|
+
# Read the text data
|
43
|
+
text_data = text_file.read()
|
44
|
+
# Encode the text data to a base64 string
|
45
|
+
base64_encoded_data = base64.b64encode(text_data.encode("utf-8"))
|
46
|
+
except UnicodeDecodeError:
|
47
|
+
# If reading as text fails, open the file in binary mode
|
48
|
+
with open(file_path, "rb") as binary_file:
|
49
|
+
# Read the binary data
|
50
|
+
binary_data = binary_file.read()
|
51
|
+
# Encode the binary data to a base64 string
|
52
|
+
base64_encoded_data = base64.b64encode(binary_data)
|
53
|
+
self.binary = True
|
54
|
+
# Convert the base64 bytes to a string
|
55
|
+
base64_string = base64_encoded_data.decode("utf-8")
|
56
|
+
|
57
|
+
return base64_string
|
58
|
+
|
59
|
+
def open(self):
|
60
|
+
if self.binary:
|
61
|
+
return self.base64_to_file(self["base64_string"], is_binary=True)
|
62
|
+
else:
|
63
|
+
return self.base64_to_text_file(self["base64_string"])
|
64
|
+
|
65
|
+
@staticmethod
|
66
|
+
def base64_to_text_file(base64_string):
|
67
|
+
# Decode the base64 string to bytes
|
68
|
+
text_data_bytes = base64.b64decode(base64_string)
|
69
|
+
|
70
|
+
# Convert bytes to string
|
71
|
+
text_data = text_data_bytes.decode("utf-8")
|
72
|
+
|
73
|
+
# Create a StringIO object from the text data
|
74
|
+
text_file = io.StringIO(text_data)
|
75
|
+
|
76
|
+
return text_file
|
77
|
+
|
78
|
+
@staticmethod
|
79
|
+
def base64_to_file(base64_string, is_binary=True):
|
80
|
+
# Decode the base64 string to bytes
|
81
|
+
file_data = base64.b64decode(base64_string)
|
82
|
+
|
83
|
+
if is_binary:
|
84
|
+
# Create a BytesIO object for binary data
|
85
|
+
return io.BytesIO(file_data)
|
86
|
+
else:
|
87
|
+
# Convert bytes to string for text data
|
88
|
+
text_data = file_data.decode("utf-8")
|
89
|
+
# Create a StringIO object for text data
|
90
|
+
return io.StringIO(text_data)
|
91
|
+
|
92
|
+
def to_tempfile(self, suffix=None):
|
93
|
+
if suffix is None:
|
94
|
+
suffix = self.suffix
|
95
|
+
if self.binary:
|
96
|
+
file_like_object = self.base64_to_file(
|
97
|
+
self["base64_string"], is_binary=True
|
98
|
+
)
|
99
|
+
else:
|
100
|
+
file_like_object = self.base64_to_text_file(self["base64_string"])
|
101
|
+
|
102
|
+
# Create a named temporary file
|
103
|
+
mode = "wb" if self.binary else "w"
|
104
|
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, mode=mode)
|
105
|
+
|
106
|
+
if self.binary:
|
107
|
+
temp_file.write(file_like_object.read())
|
108
|
+
else:
|
109
|
+
temp_file.write(file_like_object.read())
|
110
|
+
|
111
|
+
temp_file.close()
|
112
|
+
|
113
|
+
return temp_file.name
|
114
|
+
|
115
|
+
def push(self, description=None):
|
116
|
+
scenario_version = Scenario.from_dict(self.to_dict())
|
117
|
+
if description is None:
|
118
|
+
description = "File: " + self["filename"]
|
119
|
+
info = scenario_version.push(description=description)
|
120
|
+
return info
|
121
|
+
|
122
|
+
@classmethod
|
123
|
+
def pull(cls, uuid):
|
124
|
+
scenario_version = Scenario.pull(uuid)
|
125
|
+
return cls.from_dict(scenario_version.to_dict())
|
126
|
+
|
127
|
+
|
128
|
+
class CSVFileStore(FileStore):
|
129
|
+
def __init__(self, filename):
|
130
|
+
super().__init__(filename, suffix=".csv")
|
131
|
+
|
132
|
+
@classmethod
|
133
|
+
def example(cls):
|
134
|
+
from edsl.results.Results import Results
|
135
|
+
|
136
|
+
r = Results.example()
|
137
|
+
import tempfile
|
138
|
+
|
139
|
+
with tempfile.NamedTemporaryFile(suffix=".csv", delete=False) as f:
|
140
|
+
r.to_csv(filename=f.name)
|
141
|
+
return cls(f.name)
|
142
|
+
|
143
|
+
def view(self):
|
144
|
+
import pandas as pd
|
145
|
+
|
146
|
+
return pd.read_csv(self.to_tempfile())
|
147
|
+
|
148
|
+
|
149
|
+
class PDFFileStore(FileStore):
|
150
|
+
def __init__(self, filename):
|
151
|
+
super().__init__(filename, suffix=".pdf")
|
152
|
+
|
153
|
+
def view(self):
|
154
|
+
pdf_path = self.to_tempfile()
|
155
|
+
print(f"PDF path: {pdf_path}") # Print the path to ensure it exists
|
156
|
+
import os
|
157
|
+
import subprocess
|
158
|
+
|
159
|
+
if os.path.exists(pdf_path):
|
160
|
+
try:
|
161
|
+
if os.name == "posix":
|
162
|
+
# for cool kids
|
163
|
+
subprocess.run(["open", pdf_path], check=True) # macOS
|
164
|
+
elif os.name == "nt":
|
165
|
+
os.startfile(pdf_path) # Windows
|
166
|
+
else:
|
167
|
+
subprocess.run(["xdg-open", pdf_path], check=True) # Linux
|
168
|
+
except Exception as e:
|
169
|
+
print(f"Error opening PDF: {e}")
|
170
|
+
else:
|
171
|
+
print("PDF file was not created successfully.")
|
172
|
+
|
173
|
+
@classmethod
|
174
|
+
def example(cls):
|
175
|
+
import textwrap
|
176
|
+
|
177
|
+
pdf_string = textwrap.dedent(
|
178
|
+
"""\
|
179
|
+
%PDF-1.4
|
180
|
+
1 0 obj
|
181
|
+
<< /Type /Catalog /Pages 2 0 R >>
|
182
|
+
endobj
|
183
|
+
2 0 obj
|
184
|
+
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
|
185
|
+
endobj
|
186
|
+
3 0 obj
|
187
|
+
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R >>
|
188
|
+
endobj
|
189
|
+
4 0 obj
|
190
|
+
<< /Length 44 >>
|
191
|
+
stream
|
192
|
+
BT
|
193
|
+
/F1 24 Tf
|
194
|
+
100 700 Td
|
195
|
+
(Hello, World!) Tj
|
196
|
+
ET
|
197
|
+
endstream
|
198
|
+
endobj
|
199
|
+
5 0 obj
|
200
|
+
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
|
201
|
+
endobj
|
202
|
+
6 0 obj
|
203
|
+
<< /ProcSet [/PDF /Text] /Font << /F1 5 0 R >> >>
|
204
|
+
endobj
|
205
|
+
xref
|
206
|
+
0 7
|
207
|
+
0000000000 65535 f
|
208
|
+
0000000010 00000 n
|
209
|
+
0000000053 00000 n
|
210
|
+
0000000100 00000 n
|
211
|
+
0000000173 00000 n
|
212
|
+
0000000232 00000 n
|
213
|
+
0000000272 00000 n
|
214
|
+
trailer
|
215
|
+
<< /Size 7 /Root 1 0 R >>
|
216
|
+
startxref
|
217
|
+
318
|
218
|
+
%%EOF"""
|
219
|
+
)
|
220
|
+
import tempfile
|
221
|
+
|
222
|
+
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as f:
|
223
|
+
f.write(pdf_string.encode())
|
224
|
+
return cls(f.name)
|
225
|
+
|
226
|
+
|
227
|
+
class PNGFileStore(FileStore):
|
228
|
+
def __init__(self, filename):
|
229
|
+
super().__init__(filename, suffix=".png")
|
230
|
+
|
231
|
+
@classmethod
|
232
|
+
def example(cls):
|
233
|
+
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
|
+
)
|
238
|
+
import tempfile
|
239
|
+
|
240
|
+
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
|
241
|
+
f.write(png_string.encode())
|
242
|
+
return cls(f.name)
|
243
|
+
|
244
|
+
def view(self):
|
245
|
+
import matplotlib.pyplot as plt
|
246
|
+
import matplotlib.image as mpimg
|
247
|
+
|
248
|
+
img = mpimg.imread(self.to_tempfile())
|
249
|
+
plt.imshow(img)
|
250
|
+
plt.show()
|
251
|
+
|
252
|
+
|
253
|
+
class SQLiteFileStore(FileStore):
|
254
|
+
def __init__(self, filename):
|
255
|
+
super().__init__(filename, suffix=".sqlite")
|
256
|
+
|
257
|
+
@classmethod
|
258
|
+
def example(cls):
|
259
|
+
import sqlite3
|
260
|
+
import tempfile
|
261
|
+
|
262
|
+
with tempfile.NamedTemporaryFile(suffix=".sqlite", delete=False) as f:
|
263
|
+
conn = sqlite3.connect(f.name)
|
264
|
+
c = conn.cursor()
|
265
|
+
c.execute("""CREATE TABLE stocks (date text)""")
|
266
|
+
conn.commit()
|
267
|
+
|
268
|
+
def view(self):
|
269
|
+
import subprocess
|
270
|
+
import os
|
271
|
+
|
272
|
+
sqlite_path = self.to_tempfile()
|
273
|
+
os.system(f"sqlite3 {sqlite_path}")
|
274
|
+
|
275
|
+
|
276
|
+
if __name__ == "__main__":
|
277
|
+
# file_path = "../conjure/examples/Ex11-2.sav"
|
278
|
+
# fs = FileStore(file_path)
|
279
|
+
# info = fs.push()
|
280
|
+
# print(info)
|
281
|
+
|
282
|
+
# fs = CSVFileStore.example()
|
283
|
+
# fs.to_tempfile()
|
284
|
+
# print(fs.view())
|
285
|
+
|
286
|
+
# fs = PDFFileStore.example()
|
287
|
+
# fs.view()
|
288
|
+
|
289
|
+
# fs = PDFFileStore("paper.pdf")
|
290
|
+
# fs.view()
|
291
|
+
# from edsl import Conjure
|
292
|
+
|
293
|
+
fs = PNGFileStore("robot.png")
|
294
|
+
fs.view()
|
295
|
+
|
296
|
+
# c = Conjure(datafile_name=fs.to_tempfile())
|
297
|
+
# f = PDFFileStore("paper.pdf")
|
298
|
+
# print(f.to_tempfile())
|
299
|
+
# f.push()
|
edsl/scenarios/Scenario.py
CHANGED
@@ -1,26 +1,17 @@
|
|
1
1
|
"""A Scenario is a dictionary with a key/value to parameterize a question."""
|
2
2
|
|
3
|
+
from __future__ import annotations
|
3
4
|
import copy
|
4
|
-
from collections import UserDict
|
5
|
-
from typing import Union, List, Optional, Generator
|
6
5
|
import base64
|
7
6
|
import hashlib
|
8
|
-
import json
|
9
|
-
|
10
|
-
import fitz # PyMuPDF
|
11
7
|
import os
|
12
|
-
import
|
13
|
-
|
14
|
-
from
|
15
|
-
|
8
|
+
from collections import UserDict
|
9
|
+
from typing import Union, List, Optional, Generator
|
10
|
+
from uuid import uuid4
|
16
11
|
from edsl.Base import Base
|
17
12
|
from edsl.scenarios.ScenarioImageMixin import ScenarioImageMixin
|
18
13
|
from edsl.scenarios.ScenarioHtmlMixin import ScenarioHtmlMixin
|
19
|
-
|
20
|
-
from edsl.utilities.decorators import (
|
21
|
-
add_edsl_version,
|
22
|
-
remove_edsl_version,
|
23
|
-
)
|
14
|
+
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
24
15
|
|
25
16
|
|
26
17
|
class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
@@ -33,9 +24,7 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
33
24
|
|
34
25
|
:param data: A dictionary of keys/values for parameterizing questions.
|
35
26
|
"""
|
36
|
-
if data is None
|
37
|
-
data = {}
|
38
|
-
self.data = data
|
27
|
+
self.data = data if data is not None else {}
|
39
28
|
self.name = name
|
40
29
|
|
41
30
|
def replicate(self, n: int) -> "ScenarioList":
|
@@ -217,6 +206,8 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
217
206
|
|
218
207
|
@classmethod
|
219
208
|
def from_pdf(cls, pdf_path):
|
209
|
+
import fitz # PyMuPDF
|
210
|
+
|
220
211
|
# Ensure the file exists
|
221
212
|
if not os.path.exists(pdf_path):
|
222
213
|
raise FileNotFoundError(f"The file {pdf_path} does not exist.")
|
@@ -404,6 +395,8 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
404
395
|
|
405
396
|
def rich_print(self) -> "Table":
|
406
397
|
"""Display an object as a rich table."""
|
398
|
+
from rich.table import Table
|
399
|
+
|
407
400
|
table_data, column_names = self._table()
|
408
401
|
table = Table(title=f"{self.__class__.__name__} Attributes")
|
409
402
|
for column in column_names:
|
@@ -416,17 +409,16 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
416
409
|
return table
|
417
410
|
|
418
411
|
@classmethod
|
419
|
-
def example(cls) ->
|
420
|
-
"""
|
421
|
-
|
422
|
-
Example:
|
412
|
+
def example(cls, randomize: bool = False) -> Scenario:
|
413
|
+
"""
|
414
|
+
Returns an example Scenario instance.
|
423
415
|
|
424
|
-
|
425
|
-
Scenario({'persona': 'A reseacher studying whether LLMs can be used to generate surveys.'})
|
416
|
+
:param randomize: If True, adds a random string to the value of the example key.
|
426
417
|
"""
|
418
|
+
addition = "" if not randomize else str(uuid4())
|
427
419
|
return cls(
|
428
420
|
{
|
429
|
-
"persona": "A reseacher studying whether LLMs can be used to generate surveys."
|
421
|
+
"persona": f"A reseacher studying whether LLMs can be used to generate surveys.{addition}",
|
430
422
|
}
|
431
423
|
)
|
432
424
|
|
edsl/scenarios/ScenarioList.py
CHANGED
@@ -5,25 +5,20 @@ import csv
|
|
5
5
|
import random
|
6
6
|
from collections import UserList, Counter
|
7
7
|
from collections.abc import Iterable
|
8
|
-
|
9
|
-
from typing import Any, Optional, Union, List
|
10
|
-
|
11
|
-
from rich.table import Table
|
12
8
|
from simpleeval import EvalWithCompoundTypes
|
13
|
-
|
14
|
-
from edsl.scenarios.Scenario import Scenario
|
9
|
+
from typing import Any, Optional, Union, List
|
15
10
|
from edsl.Base import Base
|
16
11
|
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
12
|
+
from edsl.scenarios.Scenario import Scenario
|
17
13
|
from edsl.scenarios.ScenarioListPdfMixin import ScenarioListPdfMixin
|
14
|
+
from edsl.scenarios.ScenarioListExportMixin import ScenarioListExportMixin
|
18
15
|
|
19
|
-
from edsl.utilities.interface import print_scenario_list
|
20
16
|
|
21
|
-
|
17
|
+
class ScenarioListMixin(ScenarioListPdfMixin, ScenarioListExportMixin):
|
18
|
+
pass
|
22
19
|
|
23
|
-
from edsl.results.ResultsExportMixin import ResultsExportMixin
|
24
20
|
|
25
|
-
|
26
|
-
class ScenarioList(Base, UserList, ScenarioListPdfMixin, ResultsExportMixin):
|
21
|
+
class ScenarioList(Base, UserList, ScenarioListMixin):
|
27
22
|
"""Class for creating a list of scenarios to be used in a survey."""
|
28
23
|
|
29
24
|
def __init__(self, data: Optional[list] = None):
|
@@ -157,6 +152,8 @@ class ScenarioList(Base, UserList, ScenarioListPdfMixin, ResultsExportMixin):
|
|
157
152
|
)
|
158
153
|
raw_var_name, expression = new_var_string.split("=", 1)
|
159
154
|
var_name = raw_var_name.strip()
|
155
|
+
from edsl.utilities.utilities import is_valid_variable_name
|
156
|
+
|
160
157
|
if not is_valid_variable_name(var_name):
|
161
158
|
raise Exception(f"{var_name} is not a valid variable name.")
|
162
159
|
|
@@ -376,6 +373,8 @@ class ScenarioList(Base, UserList, ScenarioListPdfMixin, ResultsExportMixin):
|
|
376
373
|
>>> scenario_list[1]['age']
|
377
374
|
'25'
|
378
375
|
"""
|
376
|
+
from edsl.scenarios.Scenario import Scenario
|
377
|
+
|
379
378
|
observations = []
|
380
379
|
with open(filename, "r") as f:
|
381
380
|
reader = csv.reader(f)
|
@@ -413,12 +412,16 @@ class ScenarioList(Base, UserList, ScenarioListPdfMixin, ResultsExportMixin):
|
|
413
412
|
ScenarioList([Scenario({'name': 'Alice'}), Scenario({'name': 'Bob'})])
|
414
413
|
|
415
414
|
"""
|
415
|
+
from edsl.scenarios.Scenario import Scenario
|
416
|
+
|
416
417
|
return cls([Scenario(s) for s in scenario_dicts_list])
|
417
418
|
|
418
419
|
@classmethod
|
419
420
|
@remove_edsl_version
|
420
421
|
def from_dict(cls, data) -> ScenarioList:
|
421
422
|
"""Create a `ScenarioList` from a dictionary."""
|
423
|
+
from edsl.scenarios.Scenario import Scenario
|
424
|
+
|
422
425
|
return cls([Scenario.from_dict(s) for s in data["scenarios"]])
|
423
426
|
|
424
427
|
def code(self) -> str:
|
@@ -437,12 +440,18 @@ class ScenarioList(Base, UserList, ScenarioListPdfMixin, ResultsExportMixin):
|
|
437
440
|
return lines
|
438
441
|
|
439
442
|
@classmethod
|
440
|
-
def example(cls) -> ScenarioList:
|
441
|
-
"""
|
442
|
-
|
443
|
+
def example(cls, randomize: bool = False) -> ScenarioList:
|
444
|
+
"""
|
445
|
+
Return an example ScenarioList instance.
|
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)])
|
443
450
|
|
444
451
|
def rich_print(self) -> None:
|
445
452
|
"""Display an object as a table."""
|
453
|
+
from rich.table import Table
|
454
|
+
|
446
455
|
table = Table(title="ScenarioList")
|
447
456
|
table.add_column("Index", style="bold")
|
448
457
|
table.add_column("Scenario")
|
@@ -457,6 +466,8 @@ class ScenarioList(Base, UserList, ScenarioListPdfMixin, ResultsExportMixin):
|
|
457
466
|
pretty_labels: Optional[dict] = None,
|
458
467
|
filename: str = None,
|
459
468
|
):
|
469
|
+
from edsl.utilities.interface import print_scenario_list
|
470
|
+
|
460
471
|
print_scenario_list(self[:max_rows])
|
461
472
|
|
462
473
|
def __getitem__(self, key: Union[int, slice]) -> Any:
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"""Mixin class for exporting results."""
|
2
|
+
|
3
|
+
from functools import wraps
|
4
|
+
from edsl.results.DatasetExportMixin import DatasetExportMixin
|
5
|
+
|
6
|
+
|
7
|
+
def to_dataset(func):
|
8
|
+
"""Convert the Results object to a Dataset object before calling the function."""
|
9
|
+
|
10
|
+
@wraps(func)
|
11
|
+
def wrapper(self, *args, **kwargs):
|
12
|
+
"""Return the function with the Results object converted to a Dataset object."""
|
13
|
+
if self.__class__.__name__ == "ScenarioList":
|
14
|
+
return func(self.to_dataset(), *args, **kwargs)
|
15
|
+
else:
|
16
|
+
raise Exception(
|
17
|
+
f"Class {self.__class__.__name__} not recognized as a Results or Dataset object."
|
18
|
+
)
|
19
|
+
|
20
|
+
return wrapper
|
21
|
+
|
22
|
+
|
23
|
+
def decorate_all_methods(cls):
|
24
|
+
for attr_name, attr_value in cls.__dict__.items():
|
25
|
+
if callable(attr_value):
|
26
|
+
setattr(cls, attr_name, to_dataset(attr_value))
|
27
|
+
return cls
|
28
|
+
|
29
|
+
|
30
|
+
@decorate_all_methods
|
31
|
+
class ScenarioListExportMixin(DatasetExportMixin):
|
32
|
+
"""Mixin class for exporting Results objects."""
|
@@ -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,6 +22,7 @@ class ScenarioListPdfMixin:
|
|
22
22
|
"""
|
23
23
|
import tempfile
|
24
24
|
from pdf2image import convert_from_path
|
25
|
+
from edsl.scenarios import Scenario
|
25
26
|
|
26
27
|
with tempfile.TemporaryDirectory() as output_folder:
|
27
28
|
# Convert PDF to images
|
edsl/scenarios/__init__.py
CHANGED
edsl/study/Study.py
CHANGED
@@ -6,14 +6,12 @@ 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
|
9
10
|
from edsl import Cache, set_session_cache, unset_session_cache
|
10
11
|
from edsl.utilities.utilities import dict_hash
|
11
12
|
from edsl.study.ObjectEntry import ObjectEntry
|
12
13
|
from edsl.study.ProofOfWork import ProofOfWork
|
13
14
|
from edsl.study.SnapShot import SnapShot
|
14
|
-
from uuid import UUID
|
15
|
-
|
16
|
-
# from edsl.Base import Base
|
17
15
|
|
18
16
|
|
19
17
|
class Study:
|
@@ -402,14 +400,14 @@ class Study:
|
|
402
400
|
return diff
|
403
401
|
|
404
402
|
@classmethod
|
405
|
-
def example(cls, verbose=False):
|
403
|
+
def example(cls, verbose=False, randomize=False):
|
406
404
|
import tempfile
|
407
405
|
|
408
406
|
study_file = tempfile.NamedTemporaryFile()
|
409
407
|
with cls(filename=study_file.name, verbose=verbose) as study:
|
410
408
|
from edsl import QuestionFreeText
|
411
409
|
|
412
|
-
q = QuestionFreeText.example()
|
410
|
+
q = QuestionFreeText.example(randomize=randomize)
|
413
411
|
return study
|
414
412
|
|
415
413
|
@classmethod
|
@@ -463,13 +461,13 @@ class Study:
|
|
463
461
|
else:
|
464
462
|
self.objects[oe.hash] = oe
|
465
463
|
|
466
|
-
def push(self
|
464
|
+
def push(self) -> dict:
|
467
465
|
"""Push the objects to coop."""
|
468
466
|
|
469
467
|
from edsl import Coop
|
470
468
|
|
471
469
|
coop = Coop()
|
472
|
-
coop.create(self, description=self.description)
|
470
|
+
return coop.create(self, description=self.description)
|
473
471
|
|
474
472
|
@classmethod
|
475
473
|
def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
|
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
|
-
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,6 +61,8 @@ 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
|
+
|
64
66
|
self._check_valid_question_name(focal_question)
|
65
67
|
|
66
68
|
if focal_question not in self:
|
@@ -121,6 +123,7 @@ class MemoryPlan(UserDict):
|
|
121
123
|
self._check_valid_question_name(focal_question)
|
122
124
|
self._check_valid_question_name(prior_question)
|
123
125
|
self._check_order(focal_question, prior_question)
|
126
|
+
from edsl.surveys.Memory import Memory
|
124
127
|
|
125
128
|
if focal_question not in self:
|
126
129
|
memory = Memory()
|
@@ -160,6 +163,8 @@ class MemoryPlan(UserDict):
|
|
160
163
|
@classmethod
|
161
164
|
def from_dict(cls, data) -> "MemoryPlan":
|
162
165
|
"""Deserialize a memory plan from a dictionary."""
|
166
|
+
from edsl.surveys.Memory import Memory
|
167
|
+
|
163
168
|
newdata = {}
|
164
169
|
for question_name, memory in data["data"].items():
|
165
170
|
newdata[question_name] = Memory.from_dict(memory)
|
@@ -182,13 +187,15 @@ class MemoryPlan(UserDict):
|
|
182
187
|
return new_d
|
183
188
|
|
184
189
|
@property
|
185
|
-
def dag(self) -> DAG:
|
190
|
+
def dag(self) -> "DAG":
|
186
191
|
"""Return a directed acyclic graph of the memory plan.
|
187
192
|
|
188
193
|
>>> mp = MemoryPlan.example()
|
189
194
|
>>> mp.dag
|
190
195
|
{1: {0}}
|
191
196
|
"""
|
197
|
+
from edsl.surveys.DAG import DAG
|
198
|
+
|
192
199
|
d = defaultdict(set)
|
193
200
|
for focal_question, memory in self.items():
|
194
201
|
for prior_question in memory:
|