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.
Files changed (65) hide show
  1. edsl/Base.py +6 -3
  2. edsl/__init__.py +23 -23
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +43 -40
  5. edsl/agents/AgentList.py +23 -22
  6. edsl/agents/Invigilator.py +19 -2
  7. edsl/agents/descriptors.py +2 -1
  8. edsl/base/Base.py +289 -0
  9. edsl/config.py +2 -1
  10. edsl/conversation/car_buying.py +1 -1
  11. edsl/coop/utils.py +28 -1
  12. edsl/data/Cache.py +41 -18
  13. edsl/data/CacheEntry.py +6 -7
  14. edsl/data/SQLiteDict.py +11 -3
  15. edsl/data_transfer_models.py +4 -0
  16. edsl/jobs/Answers.py +15 -1
  17. edsl/jobs/Jobs.py +86 -33
  18. edsl/jobs/buckets/ModelBuckets.py +14 -2
  19. edsl/jobs/buckets/TokenBucket.py +32 -5
  20. edsl/jobs/interviews/Interview.py +99 -79
  21. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +18 -24
  22. edsl/jobs/runners/JobsRunnerAsyncio.py +16 -16
  23. edsl/jobs/tasks/QuestionTaskCreator.py +10 -6
  24. edsl/jobs/tasks/TaskHistory.py +4 -3
  25. edsl/language_models/LanguageModel.py +17 -17
  26. edsl/language_models/ModelList.py +1 -1
  27. edsl/language_models/repair.py +8 -7
  28. edsl/notebooks/Notebook.py +16 -10
  29. edsl/questions/QuestionBase.py +6 -2
  30. edsl/questions/QuestionBudget.py +5 -6
  31. edsl/questions/QuestionCheckBox.py +7 -3
  32. edsl/questions/QuestionExtract.py +5 -3
  33. edsl/questions/QuestionFreeText.py +7 -5
  34. edsl/questions/QuestionFunctional.py +34 -5
  35. edsl/questions/QuestionList.py +3 -4
  36. edsl/questions/QuestionMultipleChoice.py +68 -12
  37. edsl/questions/QuestionNumerical.py +4 -3
  38. edsl/questions/QuestionRank.py +5 -3
  39. edsl/questions/__init__.py +4 -3
  40. edsl/questions/descriptors.py +46 -4
  41. edsl/results/DatasetExportMixin.py +570 -0
  42. edsl/results/Result.py +66 -70
  43. edsl/results/Results.py +160 -68
  44. edsl/results/ResultsDBMixin.py +7 -3
  45. edsl/results/ResultsExportMixin.py +22 -537
  46. edsl/results/ResultsGGMixin.py +3 -3
  47. edsl/results/ResultsToolsMixin.py +1 -4
  48. edsl/scenarios/FileStore.py +299 -0
  49. edsl/scenarios/Scenario.py +16 -24
  50. edsl/scenarios/ScenarioList.py +25 -14
  51. edsl/scenarios/ScenarioListExportMixin.py +32 -0
  52. edsl/scenarios/ScenarioListPdfMixin.py +2 -1
  53. edsl/scenarios/__init__.py +1 -0
  54. edsl/study/Study.py +5 -7
  55. edsl/surveys/MemoryPlan.py +11 -4
  56. edsl/surveys/Survey.py +52 -15
  57. edsl/surveys/SurveyExportMixin.py +4 -2
  58. edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
  59. edsl/utilities/__init__.py +21 -21
  60. edsl/utilities/interface.py +66 -45
  61. edsl/utilities/utilities.py +11 -13
  62. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/METADATA +1 -1
  63. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/RECORD +65 -61
  64. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/WHEEL +1 -1
  65. {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()
@@ -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 subprocess
13
-
14
- from rich.table import Table
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) -> "Scenario":
420
- """Return an example scenario.
421
-
422
- Example:
412
+ def example(cls, randomize: bool = False) -> Scenario:
413
+ """
414
+ Returns an example Scenario instance.
423
415
 
424
- >>> Scenario.example()
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
 
@@ -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
- from edsl.utilities import is_valid_variable_name
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
- """Return an example of the `ScenarioList`."""
442
- return cls([Scenario.example(), Scenario.example()])
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
@@ -1 +1,2 @@
1
1
  from edsl.scenarios.Scenario import Scenario
2
+ from edsl.scenarios.ScenarioList import ScenarioList
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, refresh=False) -> None:
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):
@@ -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: