revisit 0.0.3__py2.py3-none-any.whl → 0.0.4__py2.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.
- revisit/__init__.py +6 -52
- revisit/widget.py +49 -0
- {revisit-0.0.3.dist-info → revisit-0.0.4.dist-info}/METADATA +1 -1
- revisit-0.0.4.dist-info/RECORD +9 -0
- revisit/revisit/__init__.py +0 -521
- revisit-0.0.3.dist-info/RECORD +0 -9
- /revisit/{revisit/models.py → models.py} +0 -0
- /revisit/{revisit/revisit.py → revisit.py} +0 -0
- {revisit-0.0.3.dist-info → revisit-0.0.4.dist-info}/WHEEL +0 -0
revisit/__init__.py
CHANGED
@@ -1,54 +1,8 @@
|
|
1
|
-
|
2
|
-
import
|
3
|
-
import anywidget
|
4
|
-
import traitlets
|
1
|
+
# Import main functions from core.py
|
2
|
+
from . import revisit
|
5
3
|
|
6
|
-
|
7
|
-
|
8
|
-
except importlib.metadata.PackageNotFoundError:
|
9
|
-
__version__ = "unknown"
|
4
|
+
# Import the Widget class from widget.py
|
5
|
+
from .widget import Widget
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
# class Widget2(anywidget.AnyWidget):
|
14
|
-
|
15
|
-
class TestWidget(anywidget.AnyWidget):
|
16
|
-
_esm = """
|
17
|
-
function render({ model, el }) {
|
18
|
-
let button = document.createElement("button");
|
19
|
-
button.innerHTML = `count is ${model.get("value")}`;
|
20
|
-
button.addEventListener("click", () => {
|
21
|
-
model.set("value", model.get("value") + 1);
|
22
|
-
model.save_changes();
|
23
|
-
});
|
24
|
-
model.on("change:value", () => {
|
25
|
-
button.innerHTML = `count is ${model.get("value")}`;
|
26
|
-
});
|
27
|
-
el.classList.add("counter-widget");
|
28
|
-
el.appendChild(button);
|
29
|
-
}
|
30
|
-
export default { render };
|
31
|
-
"""
|
32
|
-
_css = """
|
33
|
-
.counter-widget button { color: white; font-size: 1.75rem; background-color: #ea580c; padding: 0.5rem 1rem; border: none; border-radius: 0.25rem; }
|
34
|
-
.counter-widget button:hover { background-color: #9a3412; }
|
35
|
-
"""
|
36
|
-
value = traitlets.Int(0).tag(sync=True)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
class Widget(anywidget.AnyWidget):
|
41
|
-
_esm = pathlib.Path(__file__).parent / "static" / "widget.js"
|
42
|
-
_css = pathlib.Path(__file__).parent / "static" / "widget.css"
|
43
|
-
# value = traitlets.Int(0).tag(sync=True)
|
44
|
-
config = traitlets.Dict({}).tag(sync=True)
|
45
|
-
sequence = traitlets.List([]).tag(sync=True)
|
46
|
-
internalWidget = TestWidget()
|
47
|
-
|
48
|
-
@traitlets.observe('sequence')
|
49
|
-
def _sequence_changed(self, change):
|
50
|
-
self.internalWidget.value += 1
|
51
|
-
# internalWidget.value += 1
|
52
|
-
# print("{name} changed from {old} to {new}".format(**change))
|
53
|
-
|
54
|
-
|
7
|
+
# Expose them in the package's namespace
|
8
|
+
__all__ = [*revisit.__all__, "Widget"]
|
revisit/widget.py
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
import importlib.metadata
|
2
|
+
import pathlib
|
3
|
+
import anywidget # type: ignore
|
4
|
+
import traitlets # type: ignore
|
5
|
+
|
6
|
+
try:
|
7
|
+
__version__ = importlib.metadata.version("revisit_notebook_widget")
|
8
|
+
except importlib.metadata.PackageNotFoundError:
|
9
|
+
__version__ = "unknown"
|
10
|
+
|
11
|
+
|
12
|
+
# class Widget2(anywidget.AnyWidget):
|
13
|
+
class TestWidget(anywidget.AnyWidget):
|
14
|
+
_esm = """
|
15
|
+
function render({ model, el }) {
|
16
|
+
let button = document.createElement("button");
|
17
|
+
button.innerHTML = `count is ${model.get("value")}`;
|
18
|
+
button.addEventListener("click", () => {
|
19
|
+
model.set("value", model.get("value") + 1);
|
20
|
+
model.save_changes();
|
21
|
+
});
|
22
|
+
model.on("change:value", () => {
|
23
|
+
button.innerHTML = `count is ${model.get("value")}`;
|
24
|
+
});
|
25
|
+
el.classList.add("counter-widget");
|
26
|
+
el.appendChild(button);
|
27
|
+
}
|
28
|
+
export default { render };
|
29
|
+
"""
|
30
|
+
_css = """
|
31
|
+
.counter-widget button { color: white; font-size: 1.75rem; background-color: #ea580c; padding: 0.5rem 1rem; border: none; border-radius: 0.25rem; }
|
32
|
+
.counter-widget button:hover { background-color: #9a3412; }
|
33
|
+
"""
|
34
|
+
value = traitlets.Int(0).tag(sync=True)
|
35
|
+
|
36
|
+
|
37
|
+
class Widget(anywidget.AnyWidget):
|
38
|
+
_esm = pathlib.Path(__file__).parent / "static" / "widget.js"
|
39
|
+
_css = pathlib.Path(__file__).parent / "static" / "widget.css"
|
40
|
+
# value = traitlets.Int(0).tag(sync=True)
|
41
|
+
config = traitlets.Dict({}).tag(sync=True)
|
42
|
+
sequence = traitlets.List([]).tag(sync=True)
|
43
|
+
internalWidget = TestWidget()
|
44
|
+
|
45
|
+
@traitlets.observe('sequence')
|
46
|
+
def _sequence_changed(self, change):
|
47
|
+
self.internalWidget.value += 1
|
48
|
+
# internalWidget.value += 1
|
49
|
+
# print("{name} changed from {old} to {new}".format(**change))
|
@@ -0,0 +1,9 @@
|
|
1
|
+
revisit/__init__.py,sha256=HlkO6ZGRm7gql0cU-oQvXTbcXGBdc0ck3yvuzu7_JX8,209
|
2
|
+
revisit/models.py,sha256=c-Hsd6XqeIP-hybH6MUovHG65XXueRhaMEVJZW1ViX0,120701
|
3
|
+
revisit/revisit.py,sha256=ChFtPmdXR8WPtSoUz9-NfvOcLVutSIJWUoWYxdhTE6o,19167
|
4
|
+
revisit/widget.py,sha256=ih0XhRjkih8vpeDr1SO8l8zMydZHLSXxVaFzvA8up_8,1722
|
5
|
+
revisit/static/widget.css,sha256=TLu5F6k0CvowQtmApPswG-JZUXYszo7a10dVWKnZsIg,647
|
6
|
+
revisit/static/widget.js,sha256=MlZ2jHlh_ADsUKMC5nZaSAmW73-4y2B57NcS3E5Jh3Q,187153
|
7
|
+
revisit-0.0.4.dist-info/METADATA,sha256=uzoyn8KDCUHZpGeHNgIRDC5i1so4Wr6qyfLO1H8tk9A,1261
|
8
|
+
revisit-0.0.4.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
9
|
+
revisit-0.0.4.dist-info/RECORD,,
|
revisit/revisit/__init__.py
DELETED
@@ -1,521 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
import json
|
3
|
-
from . import models as rvt_models
|
4
|
-
from pydantic import BaseModel, ValidationError # type: ignore
|
5
|
-
from typing import List, Literal, get_origin, Optional, get_args, Any, Unpack, overload, get_type_hints
|
6
|
-
from enum import Enum
|
7
|
-
import csv
|
8
|
-
from dataclasses import make_dataclass
|
9
|
-
import re
|
10
|
-
|
11
|
-
|
12
|
-
class _JSONableBaseModel(BaseModel):
|
13
|
-
def __str__(self):
|
14
|
-
return json.dumps(
|
15
|
-
json.loads(
|
16
|
-
self.root.model_dump_json(
|
17
|
-
exclude_none=True, by_alias=True
|
18
|
-
)),
|
19
|
-
indent=4
|
20
|
-
)
|
21
|
-
|
22
|
-
|
23
|
-
# Private
|
24
|
-
class _WrappedResponse(_JSONableBaseModel):
|
25
|
-
root: rvt_models.Response
|
26
|
-
|
27
|
-
def model_post_init(self, __context: Any) -> None:
|
28
|
-
# Sets the root to be the instantiation of the individual response type instead
|
29
|
-
# of the union response type
|
30
|
-
self.root = self.root.root
|
31
|
-
|
32
|
-
def set(self, overwrite=True, **kwargs) -> _WrappedResponse:
|
33
|
-
for key, value in kwargs.items():
|
34
|
-
# Disallow changing type
|
35
|
-
if key == 'type':
|
36
|
-
if getattr(self.root, key) != value:
|
37
|
-
raise RevisitError(message=f"Cannot change type from {getattr(self.root, key)} to {value}")
|
38
|
-
elif key != 'base':
|
39
|
-
if overwrite is True or (overwrite is False and getattr(self.root, key) is None):
|
40
|
-
setattr(self.root, key, value)
|
41
|
-
|
42
|
-
# Re-validates the model. Returns the new model.
|
43
|
-
self.root = _validate_response(self.root.__dict__)
|
44
|
-
return self
|
45
|
-
|
46
|
-
|
47
|
-
# Private
|
48
|
-
class _WrappedComponent(_JSONableBaseModel):
|
49
|
-
component_name__: str
|
50
|
-
base__: Optional[_WrappedComponent] = None
|
51
|
-
context__: Optional[dict] = None
|
52
|
-
root: rvt_models.IndividualComponent
|
53
|
-
|
54
|
-
def model_post_init(self, __context: Any) -> None:
|
55
|
-
# Sets the root to be the instantiation of the individual response type instead
|
56
|
-
# of the union response type
|
57
|
-
self.root = self.root.root
|
58
|
-
|
59
|
-
def responses(self, responses: List[_WrappedResponse]) -> _WrappedComponent:
|
60
|
-
for item in responses:
|
61
|
-
if not isinstance(item, _WrappedResponse):
|
62
|
-
raise ValueError(f'Expecting type Response got {type(item)}')
|
63
|
-
self.root.response = responses
|
64
|
-
return self
|
65
|
-
|
66
|
-
def get_response(self, id: str) -> _WrappedResponse | None:
|
67
|
-
for response in self.root.response:
|
68
|
-
if response.root.id == id:
|
69
|
-
return response
|
70
|
-
return None
|
71
|
-
|
72
|
-
def edit_response(self, id: str, **kwargs) -> _WrappedComponent:
|
73
|
-
for response in self.root.response:
|
74
|
-
if response.root.id == id:
|
75
|
-
response.set(**kwargs)
|
76
|
-
return self
|
77
|
-
|
78
|
-
raise ValueError('No response with given ID found.')
|
79
|
-
|
80
|
-
def response_context(self, **kwargs):
|
81
|
-
self.context__ = kwargs
|
82
|
-
|
83
|
-
for type, data in self.context__.items():
|
84
|
-
for response in self.root.response:
|
85
|
-
if response.root.type == type or type == 'all':
|
86
|
-
response.set(
|
87
|
-
overwrite=False,
|
88
|
-
**data
|
89
|
-
)
|
90
|
-
|
91
|
-
return self
|
92
|
-
|
93
|
-
|
94
|
-
class _WrappedStudyMetadata(_JSONableBaseModel):
|
95
|
-
root: rvt_models.StudyMetadata
|
96
|
-
|
97
|
-
|
98
|
-
class _WrappedUIConfig(_JSONableBaseModel):
|
99
|
-
root: rvt_models.UIConfig
|
100
|
-
|
101
|
-
|
102
|
-
class _WrappedComponentBlock(_JSONableBaseModel):
|
103
|
-
root: rvt_models.ComponentBlock
|
104
|
-
component_objects__: List[_WrappedComponent]
|
105
|
-
|
106
|
-
def __add__(self, other):
|
107
|
-
"""Allows addition operator to append to sequence components list."""
|
108
|
-
if isinstance(other, _WrappedComponentBlock) or isinstance(other, _WrappedComponent):
|
109
|
-
self.component_objects__.append(other)
|
110
|
-
self.root.components.append(other.component_name__)
|
111
|
-
return self
|
112
|
-
return NotImplemented
|
113
|
-
|
114
|
-
def from_data(self, data_list: list):
|
115
|
-
return DataIterator(data_list, self)
|
116
|
-
|
117
|
-
|
118
|
-
class _WrappedStudyConfig(_JSONableBaseModel):
|
119
|
-
root: rvt_models.StudyConfig
|
120
|
-
|
121
|
-
|
122
|
-
class _StudyConfigType(rvt_models.StudyConfigType):
|
123
|
-
components: List[_WrappedComponent]
|
124
|
-
|
125
|
-
|
126
|
-
class DataIterator:
|
127
|
-
def __init__(self, data_list: List, parent_class: _WrappedComponentBlock):
|
128
|
-
self.data = data_list
|
129
|
-
self.parent_class = parent_class
|
130
|
-
|
131
|
-
def component(self, **kwargs):
|
132
|
-
for datum in self.data:
|
133
|
-
current_dict = {}
|
134
|
-
for key, value in kwargs.items():
|
135
|
-
if key == 'parameters':
|
136
|
-
param_dict = {}
|
137
|
-
for param_key, param_value in value.items():
|
138
|
-
if type(param_value) is str:
|
139
|
-
param_datum_value = _extract_datum_value(param_value)
|
140
|
-
if param_datum_value is not None:
|
141
|
-
param_dict[param_key] = getattr(datum, param_datum_value)
|
142
|
-
else:
|
143
|
-
param_dict[param_key] = value
|
144
|
-
else:
|
145
|
-
param_dict[param_key] = value
|
146
|
-
current_dict[key] = param_dict
|
147
|
-
else:
|
148
|
-
if type(value) is str:
|
149
|
-
datum_value = _extract_datum_value(value)
|
150
|
-
if datum_value is not None:
|
151
|
-
if key == 'component_name__':
|
152
|
-
current_dict[key] = str(getattr(datum, datum_value))
|
153
|
-
else:
|
154
|
-
current_dict[key] = getattr(datum, datum_value)
|
155
|
-
else:
|
156
|
-
current_dict[key] = value
|
157
|
-
else:
|
158
|
-
current_dict[key] = value
|
159
|
-
curr_component = component(**current_dict)
|
160
|
-
self.parent_class = self.parent_class + curr_component
|
161
|
-
# Return the parent class calling iterator when component is finished.
|
162
|
-
return self.parent_class
|
163
|
-
|
164
|
-
|
165
|
-
# # -----------------------------------
|
166
|
-
# # Factory Functions
|
167
|
-
# # -----------------------------------
|
168
|
-
|
169
|
-
# Component factory function
|
170
|
-
# Allows additional items to be sent over to our Component model while keeping restrictions
|
171
|
-
# for the model that is auto-generated.
|
172
|
-
|
173
|
-
@overload
|
174
|
-
def component(**kwargs: Unpack[rvt_models.MarkdownComponentType]) -> _WrappedComponent: ...
|
175
|
-
@overload
|
176
|
-
def component(**kwargs: Unpack[rvt_models.ReactComponentType]) -> _WrappedComponent: ...
|
177
|
-
@overload
|
178
|
-
def component(**kwargs: Unpack[rvt_models.ImageComponentType]) -> _WrappedComponent: ...
|
179
|
-
@overload
|
180
|
-
def component(**kwargs: Unpack[rvt_models.WebsiteComponentType]) -> _WrappedComponent: ...
|
181
|
-
@overload
|
182
|
-
def component(**kwargs: Unpack[rvt_models.QuestionnaireComponentType]) -> _WrappedComponent: ...
|
183
|
-
@overload
|
184
|
-
def component(**kwargs: Any) -> _WrappedComponent: ...
|
185
|
-
|
186
|
-
|
187
|
-
def component(**kwargs) -> _WrappedComponent:
|
188
|
-
# Inherit base
|
189
|
-
base_component = kwargs.get('base__', None)
|
190
|
-
if base_component:
|
191
|
-
base_fields = vars(base_component.root)
|
192
|
-
for key, value in base_fields.items():
|
193
|
-
if key not in kwargs:
|
194
|
-
kwargs[key] = value
|
195
|
-
# Get kwargs to pass to individual component
|
196
|
-
filter_kwargs = _get_filtered_kwargs(rvt_models.IndividualComponent, kwargs)
|
197
|
-
# Grab response list
|
198
|
-
response = filter_kwargs.get('response')
|
199
|
-
|
200
|
-
# Sets default response list
|
201
|
-
valid_response = []
|
202
|
-
# If response present
|
203
|
-
if response is not None:
|
204
|
-
for r in response:
|
205
|
-
|
206
|
-
# Prevent dict input
|
207
|
-
if isinstance(r, dict):
|
208
|
-
raise RevisitError(message='Cannot pass a dictionary directly into "Response" list.')
|
209
|
-
|
210
|
-
response_type_hint = get_type_hints(rvt_models.Response).get('root')
|
211
|
-
response_types = get_args(response_type_hint)
|
212
|
-
|
213
|
-
# If wrapped, get root
|
214
|
-
if isinstance(r, _WrappedResponse):
|
215
|
-
valid_response.append(r.root)
|
216
|
-
|
217
|
-
# If not wrapped but is valid response, append to list
|
218
|
-
elif r.__class__ in response_types:
|
219
|
-
valid_response.append(r)
|
220
|
-
|
221
|
-
# If other unknown type, raise error
|
222
|
-
else:
|
223
|
-
raise RevisitError(message=f'Invalid type {type(r)} for "Response" class.')
|
224
|
-
|
225
|
-
filter_kwargs['response'] = valid_response
|
226
|
-
|
227
|
-
# Validate component
|
228
|
-
_validate_component(filter_kwargs)
|
229
|
-
base_model = rvt_models.IndividualComponent(**filter_kwargs)
|
230
|
-
|
231
|
-
try:
|
232
|
-
return _WrappedComponent(**kwargs, root=base_model)
|
233
|
-
except ValidationError as e:
|
234
|
-
raise RevisitError(e.errors())
|
235
|
-
|
236
|
-
|
237
|
-
# Response factory function
|
238
|
-
@overload
|
239
|
-
def response(**kwargs: Unpack[rvt_models.NumericalResponseType]) -> _WrappedResponse: ...
|
240
|
-
@overload
|
241
|
-
def response(**kwargs: Unpack[rvt_models.ShortTextResponseType]) -> _WrappedResponse: ...
|
242
|
-
@overload
|
243
|
-
def response(**kwargs: Unpack[rvt_models.LongTextResponseType]) -> _WrappedResponse: ...
|
244
|
-
@overload
|
245
|
-
def response(**kwargs: Unpack[rvt_models.LikertResponseType]) -> _WrappedResponse: ...
|
246
|
-
@overload
|
247
|
-
def response(**kwargs: Unpack[rvt_models.DropdownResponseType]) -> _WrappedResponse: ...
|
248
|
-
@overload
|
249
|
-
def response(**kwargs: Unpack[rvt_models.SliderResponseType]) -> _WrappedResponse: ...
|
250
|
-
@overload
|
251
|
-
def response(**kwargs: Unpack[rvt_models.RadioResponseType]) -> _WrappedResponse: ...
|
252
|
-
@overload
|
253
|
-
def response(**kwargs: Unpack[rvt_models.CheckboxResponseType]) -> _WrappedResponse: ...
|
254
|
-
@overload
|
255
|
-
def response(**kwargs: Unpack[rvt_models.IFrameResponseType]) -> _WrappedResponse: ...
|
256
|
-
@overload
|
257
|
-
def response(**kwargs: Unpack[rvt_models.MatrixResponseType]) -> _WrappedResponse: ...
|
258
|
-
@overload
|
259
|
-
def response(**kwargs: Any) -> _WrappedResponse: ...
|
260
|
-
|
261
|
-
|
262
|
-
def response(**kwargs) -> _WrappedResponse:
|
263
|
-
filter_kwargs = _get_filtered_kwargs(rvt_models.Response, kwargs)
|
264
|
-
_validate_response(filter_kwargs)
|
265
|
-
base_model = rvt_models.Response(**filter_kwargs)
|
266
|
-
# We've validated the response for a particular type. Now, how do we validate the wrapped component correctly?
|
267
|
-
try:
|
268
|
-
return _WrappedResponse(**kwargs, root=base_model)
|
269
|
-
except ValidationError as e:
|
270
|
-
raise RevisitError(e.errors())
|
271
|
-
|
272
|
-
|
273
|
-
def studyMetadata(**kwargs: Unpack[rvt_models.StudyMetadataType]):
|
274
|
-
filter_kwargs = _get_filtered_kwargs(rvt_models.StudyMetadata, kwargs)
|
275
|
-
base_model = rvt_models.StudyMetadata(**filter_kwargs)
|
276
|
-
return _WrappedStudyMetadata(**kwargs, root=base_model)
|
277
|
-
|
278
|
-
|
279
|
-
def uiConfig(**kwargs: Unpack[rvt_models.UIConfigType]):
|
280
|
-
filter_kwargs = _get_filtered_kwargs(rvt_models.UIConfig, kwargs)
|
281
|
-
base_model = rvt_models.UIConfig(**filter_kwargs)
|
282
|
-
return _WrappedUIConfig(**kwargs, root=base_model)
|
283
|
-
|
284
|
-
|
285
|
-
def sequence(**kwargs: Unpack[rvt_models.ComponentBlockType]):
|
286
|
-
filter_kwargs = _get_filtered_kwargs(rvt_models.ComponentBlock, kwargs)
|
287
|
-
valid_component_names = []
|
288
|
-
valid_components = []
|
289
|
-
components = filter_kwargs.get('components')
|
290
|
-
if components is not None:
|
291
|
-
for c in components:
|
292
|
-
|
293
|
-
# Prevent dict input
|
294
|
-
if isinstance(c, dict):
|
295
|
-
raise RevisitError(message='Cannot pass a dictionary directly into "Component" list.')
|
296
|
-
|
297
|
-
# If wrapped, get root
|
298
|
-
if isinstance(c, _WrappedComponent):
|
299
|
-
valid_component_names.append(c.component_name__)
|
300
|
-
valid_components.append(c)
|
301
|
-
|
302
|
-
# If other unknown type, raise error
|
303
|
-
else:
|
304
|
-
raise RevisitError(message=f'Invalid type {type(c)} for "Component" class.')
|
305
|
-
|
306
|
-
filter_kwargs['components'] = valid_component_names
|
307
|
-
base_model = rvt_models.ComponentBlock(**filter_kwargs)
|
308
|
-
return _WrappedComponentBlock(**kwargs, root=base_model, component_objects__=valid_components)
|
309
|
-
|
310
|
-
|
311
|
-
@overload
|
312
|
-
def studyConfig(**kwargs: Unpack[_StudyConfigType]) -> _WrappedStudyConfig: ...
|
313
|
-
@overload
|
314
|
-
def studyConfig(**kwargs: Any) -> _WrappedStudyConfig: ...
|
315
|
-
|
316
|
-
|
317
|
-
def studyConfig(**kwargs: Unpack[_StudyConfigType]) -> _WrappedStudyConfig:
|
318
|
-
filter_kwargs = _get_filtered_kwargs(rvt_models.StudyConfig, kwargs)
|
319
|
-
|
320
|
-
root_list = ['studyMetadata', 'uiConfig', 'sequence']
|
321
|
-
un_rooted_kwargs = {x: (y.root if x in root_list and hasattr(y, 'root') else y) for x, y in filter_kwargs.items()}
|
322
|
-
|
323
|
-
study_sequence = filter_kwargs['sequence']
|
324
|
-
|
325
|
-
# Merges components from the components list given and the components that are stored in the sequence
|
326
|
-
un_rooted_kwargs['components'] = {
|
327
|
-
comp.component_name__: comp.root for comp in un_rooted_kwargs.get('components', [])
|
328
|
-
} | {
|
329
|
-
comp.component_name__: comp.root for comp in study_sequence.component_objects__
|
330
|
-
}
|
331
|
-
|
332
|
-
base_model = rvt_models.StudyConfig(**un_rooted_kwargs)
|
333
|
-
return _WrappedStudyConfig(**kwargs, root=base_model)
|
334
|
-
|
335
|
-
|
336
|
-
# Function to parse the CSV and dynamically create data classes
|
337
|
-
def data(file_path: str) -> List[Any]:
|
338
|
-
# Read the first row to get the headers
|
339
|
-
with open(file_path, mode='r') as csvfile:
|
340
|
-
csv_reader = csv.DictReader(csvfile)
|
341
|
-
headers = csv_reader.fieldnames
|
342
|
-
if not headers:
|
343
|
-
raise RevisitError(message="No headers found in CSV file.")
|
344
|
-
|
345
|
-
# Create a data class with attributes based on the headers
|
346
|
-
DataRow = make_dataclass("DataRow", [(header, Any) for header in headers])
|
347
|
-
|
348
|
-
# Parse each row into an instance of the dynamically created data class
|
349
|
-
data_rows = []
|
350
|
-
for row in csv_reader:
|
351
|
-
# Convert the row values to the appropriate types (e.g., int, float, bool)
|
352
|
-
data = {key: _convert_value(value) for key, value in row.items()}
|
353
|
-
data_row = DataRow(**data)
|
354
|
-
data_rows.append(data_row)
|
355
|
-
|
356
|
-
return data_rows
|
357
|
-
|
358
|
-
|
359
|
-
# ------- PRIVATE FUNCTIONS ------------ #
|
360
|
-
|
361
|
-
def _validate_component(kwargs: dict):
|
362
|
-
component_mapping = _generate_possible_component_types()[1]
|
363
|
-
if 'type' not in kwargs:
|
364
|
-
raise RevisitError(message='"Type" is required on Component.')
|
365
|
-
elif component_mapping.get(kwargs['type']) is None:
|
366
|
-
raise RevisitError(message=f'Unexpected component type: {kwargs['type']}')
|
367
|
-
|
368
|
-
try:
|
369
|
-
return rvt_models.IndividualComponent.model_validate(kwargs).root
|
370
|
-
except ValidationError as e:
|
371
|
-
temp_errors = []
|
372
|
-
|
373
|
-
for entry in e.errors():
|
374
|
-
if entry['loc'][0] == component_mapping[kwargs['type']]:
|
375
|
-
temp_errors.append(entry)
|
376
|
-
|
377
|
-
if len(temp_errors) > 0:
|
378
|
-
raise RevisitError(temp_errors)
|
379
|
-
else:
|
380
|
-
raise RevisitError(
|
381
|
-
message='Unexpected error occurred during Component instantiation.'
|
382
|
-
)
|
383
|
-
|
384
|
-
|
385
|
-
# Call validate response when creating response component.
|
386
|
-
def _validate_response(kwargs: dict):
|
387
|
-
response_mapping = _generate_possible_response_types()[1]
|
388
|
-
if 'type' not in kwargs:
|
389
|
-
raise RevisitError(message='"Type" is required on Response.')
|
390
|
-
else:
|
391
|
-
|
392
|
-
type_value = kwargs.get('type')
|
393
|
-
|
394
|
-
# Handles enum class type
|
395
|
-
if isinstance(kwargs.get('type'), Enum):
|
396
|
-
type_value = type_value.value
|
397
|
-
|
398
|
-
if response_mapping.get(type_value) is None:
|
399
|
-
raise RevisitError(message=f'Unexpected type: {type_value}')
|
400
|
-
|
401
|
-
try:
|
402
|
-
return rvt_models.Response.model_validate(kwargs).root
|
403
|
-
except ValidationError as e:
|
404
|
-
temp_errors = []
|
405
|
-
for entry in e.errors():
|
406
|
-
if entry['loc'][0] == response_mapping[type_value]:
|
407
|
-
temp_errors.append(entry)
|
408
|
-
|
409
|
-
if len(temp_errors) > 0:
|
410
|
-
raise RevisitError(temp_errors)
|
411
|
-
else:
|
412
|
-
raise RevisitError(
|
413
|
-
message='Unexpected error occurred during Response instantiation'
|
414
|
-
)
|
415
|
-
|
416
|
-
|
417
|
-
def _generate_possible_response_types():
|
418
|
-
return _generate_possible_types(rvt_models.Response)
|
419
|
-
|
420
|
-
|
421
|
-
def _generate_possible_component_types():
|
422
|
-
return _generate_possible_types(rvt_models.IndividualComponent)
|
423
|
-
|
424
|
-
|
425
|
-
# Generates mappings between the response class name and the
|
426
|
-
# type string literal. Creates the reversed mapping as well.
|
427
|
-
def _generate_possible_types(orig_cls):
|
428
|
-
response_type_hint = get_type_hints(orig_cls).get('root')
|
429
|
-
response_types = get_args(response_type_hint)
|
430
|
-
type_hints = {}
|
431
|
-
type_hints_reversed = {}
|
432
|
-
for cls in response_types:
|
433
|
-
curr_type = get_type_hints(cls).get('type')
|
434
|
-
curr_origin = get_origin(get_type_hints(cls).get('type'))
|
435
|
-
if curr_origin is Literal:
|
436
|
-
type_hints[cls.__name__] = set([get_args(curr_type)[0]])
|
437
|
-
type_hints_reversed[get_args(curr_type)[0]] = cls.__name__
|
438
|
-
elif isinstance(curr_type, type) and issubclass(curr_type, Enum):
|
439
|
-
enum_list = [member.value for member in curr_type]
|
440
|
-
type_hints[cls.__name__] = set(enum_list)
|
441
|
-
for item in enum_list:
|
442
|
-
type_hints_reversed[item] = cls.__name__
|
443
|
-
|
444
|
-
return (type_hints, type_hints_reversed)
|
445
|
-
|
446
|
-
|
447
|
-
# Custom exception
|
448
|
-
class RevisitError(Exception):
|
449
|
-
def __init__(self, errors=None, message=None):
|
450
|
-
# Case 1: Validation Errors From Pydantic
|
451
|
-
# Case 2: Standard Error Message
|
452
|
-
super().__init__('There was an error.')
|
453
|
-
if message is None:
|
454
|
-
pretty_message_list = pretty_error(errors)
|
455
|
-
self.message = \
|
456
|
-
f'There was an error. \n' \
|
457
|
-
f'----------------------------------------------------' \
|
458
|
-
f'\n\n' \
|
459
|
-
f'{'\n\n'.join(pretty_message_list)}' \
|
460
|
-
f'\n'
|
461
|
-
else:
|
462
|
-
self.message = \
|
463
|
-
f'There was an error. \n' \
|
464
|
-
f'----------------------------------------------------' \
|
465
|
-
f'\n\n' \
|
466
|
-
f'{message}' \
|
467
|
-
f'\n'
|
468
|
-
|
469
|
-
def __str__(self):
|
470
|
-
return self.message
|
471
|
-
|
472
|
-
|
473
|
-
def pretty_error(errors):
|
474
|
-
custom_messages = {
|
475
|
-
'missing': 'Field is missing'
|
476
|
-
}
|
477
|
-
new_error_messages = []
|
478
|
-
for error in errors:
|
479
|
-
custom_message = custom_messages.get(error['type'])
|
480
|
-
if custom_message:
|
481
|
-
new_error_messages.append(f'Location: {error['loc']}\nError: Field "{error['loc'][-1]}" is required.')
|
482
|
-
else:
|
483
|
-
new_error_messages.append(f'Location: {error['loc']}\nError: {error['msg']}')
|
484
|
-
return new_error_messages
|
485
|
-
|
486
|
-
|
487
|
-
def _get_filtered_kwargs(class_type: Any, kwargs):
|
488
|
-
try:
|
489
|
-
possible_items = get_args(class_type.__fields__.get('root').annotation)
|
490
|
-
except AttributeError:
|
491
|
-
possible_items = [class_type]
|
492
|
-
|
493
|
-
valid_fields = set()
|
494
|
-
for model in possible_items:
|
495
|
-
valid_fields.update(model.model_fields.keys())
|
496
|
-
|
497
|
-
return {key: value for key, value in kwargs.items() if key in valid_fields}
|
498
|
-
|
499
|
-
|
500
|
-
def _convert_value(value: str) -> Any:
|
501
|
-
"""Helper function to convert string values to appropriate data types."""
|
502
|
-
value = value.strip()
|
503
|
-
if value.lower() == "true":
|
504
|
-
return True
|
505
|
-
elif value.lower() == "false":
|
506
|
-
return False
|
507
|
-
try:
|
508
|
-
if '.' in value:
|
509
|
-
return float(value)
|
510
|
-
else:
|
511
|
-
return int(value)
|
512
|
-
except ValueError:
|
513
|
-
return value # Return as string if it cannot be converted
|
514
|
-
|
515
|
-
|
516
|
-
def _extract_datum_value(text: str) -> str:
|
517
|
-
# Use regex to match 'datum:thing' and capture 'thing'
|
518
|
-
match = re.match(r'^datum:(\w+)$', text)
|
519
|
-
if match:
|
520
|
-
return match.group(1) # Return the captured part (i.e., 'thing')
|
521
|
-
return None # Return None if the pattern doesn't match
|
revisit-0.0.3.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
revisit/__init__.py,sha256=cDMK5FstjH7vrZQZabUmcZwpB3-hTZMHatVUu2gdKr0,1702
|
2
|
-
revisit/revisit/__init__.py,sha256=7rhJeSoVZkNSNXUVHRIFQxkuxDUn_dtcjXnC2DQLtuk,19174
|
3
|
-
revisit/revisit/models.py,sha256=c-Hsd6XqeIP-hybH6MUovHG65XXueRhaMEVJZW1ViX0,120701
|
4
|
-
revisit/revisit/revisit.py,sha256=ChFtPmdXR8WPtSoUz9-NfvOcLVutSIJWUoWYxdhTE6o,19167
|
5
|
-
revisit/static/widget.css,sha256=TLu5F6k0CvowQtmApPswG-JZUXYszo7a10dVWKnZsIg,647
|
6
|
-
revisit/static/widget.js,sha256=MlZ2jHlh_ADsUKMC5nZaSAmW73-4y2B57NcS3E5Jh3Q,187153
|
7
|
-
revisit-0.0.3.dist-info/METADATA,sha256=FF8AyslwC22_3OXEjXA1ZfE4yjzM1PsRbxx-zn6UHHo,1261
|
8
|
-
revisit-0.0.3.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
9
|
-
revisit-0.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|