edsl 0.1.29__py3-none-any.whl → 0.1.29.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/Base.py +18 -18
- edsl/__init__.py +23 -23
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +41 -77
- edsl/agents/AgentList.py +9 -19
- 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 +1 -1
- 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/Study.py +9 -3
- 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.dev2.dist-info}/METADATA +10 -11
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev2.dist-info}/RECORD +68 -71
- edsl-0.1.29.dev2.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.dev2.dist-info}/LICENSE +0 -0
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev2.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/Study.py
CHANGED
@@ -472,12 +472,18 @@ class Study:
|
|
472
472
|
coop.create(self, description=self.description)
|
473
473
|
|
474
474
|
@classmethod
|
475
|
-
def pull(cls,
|
475
|
+
def pull(cls, id_or_url: Union[str, UUID], exec_profile=None):
|
476
476
|
"""Pull the object from coop."""
|
477
477
|
from edsl.coop import Coop
|
478
478
|
|
479
|
-
|
480
|
-
|
479
|
+
if id_or_url.startswith("http"):
|
480
|
+
uuid_value = id_or_url.split("/")[-1]
|
481
|
+
else:
|
482
|
+
uuid_value = id_or_url
|
483
|
+
|
484
|
+
c = Coop()
|
485
|
+
|
486
|
+
return c._get_base(cls, uuid_value, exec_profile=exec_profile)
|
481
487
|
|
482
488
|
def __repr__(self):
|
483
489
|
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})"""
|
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
|
edsl/tools/plotting.py
CHANGED
@@ -93,10 +93,8 @@ def theme_plot(results, field, context, themes=None, progress_bar=False):
|
|
93
93
|
SELECT json_each.value AS theme
|
94
94
|
FROM self,
|
95
95
|
json_each({ field }_themes)
|
96
|
-
)
|
97
|
-
|
98
|
-
HAVING theme <> 'Other'
|
99
|
-
ORDER BY mentions DESC
|
96
|
+
) GROUP BY theme
|
97
|
+
ORDER BY mentions DESC
|
100
98
|
"""
|
101
99
|
themes = results.sql(themes_query, to_list=True)
|
102
100
|
|
edsl/utilities/__init__.py
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
from edsl.utilities.interface import (
|
2
|
+
print_dict_as_html_table,
|
3
|
+
print_dict_with_rich,
|
4
|
+
print_list_of_dicts_as_html_table,
|
5
|
+
print_table_with_rich,
|
6
|
+
print_public_methods_with_doc,
|
7
|
+
print_list_of_dicts_as_markdown_table,
|
8
|
+
)
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
from edsl.utilities.utilities import (
|
11
|
+
create_valid_var_name,
|
12
|
+
dict_to_html,
|
13
|
+
hash_value,
|
14
|
+
HTMLSnippet,
|
15
|
+
is_notebook,
|
16
|
+
is_gzipped,
|
17
|
+
is_valid_variable_name,
|
18
|
+
random_string,
|
19
|
+
repair_json,
|
20
|
+
shorten_string,
|
21
|
+
time_all_functions,
|
22
|
+
)
|
edsl/utilities/interface.py
CHANGED
@@ -1,45 +1,12 @@
|
|
1
1
|
"""A module for displaying data in various formats."""
|
2
2
|
|
3
3
|
from html import escape
|
4
|
+
from IPython.display import HTML
|
5
|
+
from IPython.display import display as ipython_diplay
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
font_size = 15
|
9
|
-
from PIL import Image, ImageDraw, ImageFont
|
10
|
-
|
11
|
-
text = console.export_text() # Get the console output as text.
|
12
|
-
|
13
|
-
# Create an image from the text
|
14
|
-
font_size = 15
|
15
|
-
font = ImageFont.load_default() # Use the default font to avoid file path issues.
|
16
|
-
# text_width, text_height = ImageDraw.Draw(
|
17
|
-
# Image.new("RGB", (100, 100))
|
18
|
-
# ).multiline_textsize(text, font=font)
|
19
|
-
text_width, text_height = get_multiline_textsize(text, font)
|
20
|
-
image = Image.new(
|
21
|
-
"RGB", (text_width + 20, text_height + 20), color=(255, 255, 255)
|
22
|
-
) # Add some padding
|
23
|
-
d = ImageDraw.Draw(image)
|
24
|
-
|
25
|
-
# Draw text to image
|
26
|
-
d.multiline_text((10, 10), text, font=font, fill=(0, 0, 0))
|
27
|
-
# Save the image
|
28
|
-
image.save(image_filename)
|
29
|
-
|
30
|
-
|
31
|
-
def display_table(console, table, filename):
|
32
|
-
# from rich.console import Console
|
33
|
-
# from rich.table import Table
|
34
|
-
"""Display the table using the rich library and save it to a file if a filename is provided."""
|
35
|
-
if filename is not None:
|
36
|
-
with open(filename, "w") as f:
|
37
|
-
with console.capture() as capture:
|
38
|
-
console.print(table)
|
39
|
-
f.write(capture.get())
|
40
|
-
create_image(console, filename + ".png")
|
41
|
-
else:
|
42
|
-
console.print(table)
|
7
|
+
# from PIL import Image, ImageDraw, ImageFont
|
8
|
+
from rich.console import Console
|
9
|
+
from rich.table import Table
|
43
10
|
|
44
11
|
|
45
12
|
def gen_html_sandwich(html_inner, interactive=False):
|
@@ -166,10 +133,43 @@ def get_multiline_textsize(text, font):
|
|
166
133
|
return max_width, total_height
|
167
134
|
|
168
135
|
|
169
|
-
def
|
170
|
-
|
171
|
-
|
136
|
+
# def create_image(console, image_filename):
|
137
|
+
# """Create an image from the console output."""
|
138
|
+
# font_size = 15
|
139
|
+
|
140
|
+
# text = console.export_text() # Get the console output as text.
|
141
|
+
|
142
|
+
# # Create an image from the text
|
143
|
+
# font_size = 15
|
144
|
+
# font = ImageFont.load_default() # Use the default font to avoid file path issues.
|
145
|
+
# # text_width, text_height = ImageDraw.Draw(
|
146
|
+
# # Image.new("RGB", (100, 100))
|
147
|
+
# # ).multiline_textsize(text, font=font)
|
148
|
+
# text_width, text_height = get_multiline_textsize(text, font)
|
149
|
+
# image = Image.new(
|
150
|
+
# "RGB", (text_width + 20, text_height + 20), color=(255, 255, 255)
|
151
|
+
# ) # Add some padding
|
152
|
+
# d = ImageDraw.Draw(image)
|
153
|
+
|
154
|
+
# # Draw text to image
|
155
|
+
# d.multiline_text((10, 10), text, font=font, fill=(0, 0, 0))
|
156
|
+
# # Save the image
|
157
|
+
# image.save(image_filename)
|
158
|
+
|
172
159
|
|
160
|
+
def display(console, table, filename):
|
161
|
+
"""Display the table using the rich library and save it to a file if a filename is provided."""
|
162
|
+
if filename is not None:
|
163
|
+
with open(filename, "w") as f:
|
164
|
+
with console.capture() as capture:
|
165
|
+
console.print(table)
|
166
|
+
f.write(capture.get())
|
167
|
+
create_image(console, filename + ".png")
|
168
|
+
else:
|
169
|
+
console.print(table)
|
170
|
+
|
171
|
+
|
172
|
+
def print_results_long(results, max_rows=None):
|
173
173
|
console = Console(record=True)
|
174
174
|
table = Table(show_header=True, header_style="bold magenta")
|
175
175
|
table.add_column("Result index", style="dim")
|
@@ -199,9 +199,6 @@ def print_dict_with_rich(d, key_name="Key", value_name="Value", filename=None):
|
|
199
199
|
│ c │ 3 │
|
200
200
|
└─────┴───────┘
|
201
201
|
"""
|
202
|
-
from rich.console import Console
|
203
|
-
from rich.table import Table
|
204
|
-
|
205
202
|
console = Console(record=True)
|
206
203
|
table = Table(show_header=True, header_style="bold magenta")
|
207
204
|
table.add_column(key_name, style="dim")
|
@@ -209,7 +206,7 @@ def print_dict_with_rich(d, key_name="Key", value_name="Value", filename=None):
|
|
209
206
|
for key, value in d.items():
|
210
207
|
table.add_row(key, str(value))
|
211
208
|
console.print(table)
|
212
|
-
#
|
209
|
+
# display(console, table, filename)
|
213
210
|
|
214
211
|
|
215
212
|
def print_dict_as_html_table(
|
@@ -254,9 +251,6 @@ def print_dict_as_html_table(
|
|
254
251
|
|
255
252
|
|
256
253
|
def print_scenario_list(data):
|
257
|
-
from rich.console import Console
|
258
|
-
from rich.table import Table
|
259
|
-
|
260
254
|
new_data = []
|
261
255
|
for obs in data:
|
262
256
|
try:
|
@@ -306,9 +300,6 @@ def print_dataset_with_rich(data, filename=None, split_at_dot=True):
|
|
306
300
|
│ 3 │ 6 │
|
307
301
|
└───┴───┘
|
308
302
|
"""
|
309
|
-
from rich.console import Console
|
310
|
-
from rich.table import Table
|
311
|
-
|
312
303
|
console = Console(record=True)
|
313
304
|
|
314
305
|
# Create a table object
|
@@ -330,7 +321,7 @@ def print_dataset_with_rich(data, filename=None, split_at_dot=True):
|
|
330
321
|
table.add_row(*row)
|
331
322
|
|
332
323
|
console.print(table)
|
333
|
-
#
|
324
|
+
# display(console, table, filename)
|
334
325
|
|
335
326
|
|
336
327
|
def create_latex_table_from_data(data, filename=None, split_at_dot=True):
|
@@ -504,9 +495,6 @@ def print_list_of_dicts_as_markdown_table(data, filename=None):
|
|
504
495
|
|
505
496
|
def print_public_methods_with_doc(obj):
|
506
497
|
"""Print the public methods of an object along with their docstrings."""
|
507
|
-
from rich.console import Console
|
508
|
-
from rich.table import Table
|
509
|
-
|
510
498
|
console = Console()
|
511
499
|
public_methods_with_docstrings = [
|
512
500
|
(method, getattr(obj, method).__doc__)
|
@@ -537,10 +525,6 @@ def print_tally_with_rich(data, filename=None):
|
|
537
525
|
└───────┴───────┘
|
538
526
|
"""
|
539
527
|
# Initialize a console object
|
540
|
-
from rich.console import Console
|
541
|
-
from rich.table import Table
|
542
|
-
from IPython.display import display
|
543
|
-
|
544
528
|
console = Console(record=True)
|
545
529
|
|
546
530
|
# Create a new table
|
@@ -554,9 +538,7 @@ def print_tally_with_rich(data, filename=None):
|
|
554
538
|
for key, value in data.items():
|
555
539
|
table.add_row(key, str(value))
|
556
540
|
|
557
|
-
|
558
|
-
|
559
|
-
display_table(console, table, filename)
|
541
|
+
display(console, table, filename)
|
560
542
|
|
561
543
|
|
562
544
|
def print_table_with_rich(data, filename=None):
|
@@ -579,9 +561,6 @@ def print_table_with_rich(data, filename=None):
|
|
579
561
|
│ 2 │ 9 │ 8 │
|
580
562
|
└───┴───┴───┘
|
581
563
|
"""
|
582
|
-
from rich.console import Console
|
583
|
-
from rich.table import Table
|
584
|
-
|
585
564
|
# Initialize a console object - expects a list of dictionaries
|
586
565
|
console = Console(record=True)
|
587
566
|
|
@@ -601,7 +580,7 @@ def print_table_with_rich(data, filename=None):
|
|
601
580
|
for row in data:
|
602
581
|
table.add_row(*[str(value) for value in row.values()])
|
603
582
|
|
604
|
-
|
583
|
+
display(console, table, filename)
|
605
584
|
|
606
585
|
|
607
586
|
if __name__ == "__main__":
|