py10x-universe 0.1.3__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.
- core_10x/__init__.py +42 -0
- core_10x/backbone/__init__.py +0 -0
- core_10x/backbone/backbone_store.py +59 -0
- core_10x/backbone/backbone_traitable.py +30 -0
- core_10x/backbone/backbone_user.py +66 -0
- core_10x/backbone/bound_data_domain.py +49 -0
- core_10x/backbone/namespace.py +101 -0
- core_10x/backbone/vault.py +38 -0
- core_10x/code_samples/__init__.py +0 -0
- core_10x/code_samples/_package_manifest.py +3 -0
- core_10x/code_samples/directories.py +181 -0
- core_10x/code_samples/person.py +76 -0
- core_10x/concrete_traits.py +356 -0
- core_10x/conftest.py +12 -0
- core_10x/curve.py +321 -0
- core_10x/data_domain.py +48 -0
- core_10x/data_domain_binder.py +45 -0
- core_10x/directory.py +250 -0
- core_10x/entity.py +8 -0
- core_10x/entity_filter.py +5 -0
- core_10x/environment_variables.py +147 -0
- core_10x/exec_control.py +84 -0
- core_10x/experimental/__init__.py +0 -0
- core_10x/experimental/data_protocol_ex.py +34 -0
- core_10x/global_cache.py +121 -0
- core_10x/manual_tests/__init__.py +0 -0
- core_10x/manual_tests/calendar_test.py +35 -0
- core_10x/manual_tests/ctor_update_bug.py +58 -0
- core_10x/manual_tests/debug_graph_on.py +17 -0
- core_10x/manual_tests/debug_graphoff_inside_graph_on.py +28 -0
- core_10x/manual_tests/enum_bits_test.py +17 -0
- core_10x/manual_tests/env_vars_trivial_test.py +12 -0
- core_10x/manual_tests/existing_traitable.py +33 -0
- core_10x/manual_tests/k10x_test1.py +13 -0
- core_10x/manual_tests/named_constant_test.py +121 -0
- core_10x/manual_tests/nucleus_trivial_test.py +42 -0
- core_10x/manual_tests/polars_test.py +14 -0
- core_10x/manual_tests/py_class_test.py +4 -0
- core_10x/manual_tests/rc_test.py +42 -0
- core_10x/manual_tests/rdate_test.py +12 -0
- core_10x/manual_tests/reference_serialization_bug.py +19 -0
- core_10x/manual_tests/resource_trivial_test.py +10 -0
- core_10x/manual_tests/store_uri_test.py +6 -0
- core_10x/manual_tests/trait_definition_test.py +19 -0
- core_10x/manual_tests/trait_filter_test.py +15 -0
- core_10x/manual_tests/trait_flag_modification_test.py +42 -0
- core_10x/manual_tests/trait_modification_bug.py +26 -0
- core_10x/manual_tests/traitable_as_of_test.py +82 -0
- core_10x/manual_tests/traitable_heir_test.py +39 -0
- core_10x/manual_tests/traitable_history_test.py +41 -0
- core_10x/manual_tests/traitable_serialization_test.py +54 -0
- core_10x/manual_tests/traitable_trivial_test.py +71 -0
- core_10x/manual_tests/trivial_graph_test.py +16 -0
- core_10x/manual_tests/ts_class_association_test.py +64 -0
- core_10x/manual_tests/ts_trivial_test.py +35 -0
- core_10x/named_constant.py +425 -0
- core_10x/nucleus.py +81 -0
- core_10x/package_manifest.py +85 -0
- core_10x/package_refactoring.py +153 -0
- core_10x/py_class.py +431 -0
- core_10x/rc.py +155 -0
- core_10x/rdate.py +339 -0
- core_10x/resource.py +189 -0
- core_10x/roman_number.py +67 -0
- core_10x/testlib/__init__.py +0 -0
- core_10x/testlib/test_store.py +240 -0
- core_10x/testlib/traitable_history_tests.py +787 -0
- core_10x/testlib/ts_tests.py +280 -0
- core_10x/trait.py +377 -0
- core_10x/trait_definition.py +176 -0
- core_10x/trait_filter.py +205 -0
- core_10x/trait_method_error.py +36 -0
- core_10x/traitable.py +1082 -0
- core_10x/traitable_cli.py +153 -0
- core_10x/traitable_heir.py +33 -0
- core_10x/traitable_id.py +31 -0
- core_10x/ts_store.py +172 -0
- core_10x/ts_store_type.py +26 -0
- core_10x/ts_union.py +147 -0
- core_10x/ui_hint.py +153 -0
- core_10x/unit_tests/test_concrete_traits.py +156 -0
- core_10x/unit_tests/test_converters.py +51 -0
- core_10x/unit_tests/test_curve.py +157 -0
- core_10x/unit_tests/test_directory.py +54 -0
- core_10x/unit_tests/test_documentation.py +172 -0
- core_10x/unit_tests/test_environment_variables.py +15 -0
- core_10x/unit_tests/test_filters.py +239 -0
- core_10x/unit_tests/test_graph.py +348 -0
- core_10x/unit_tests/test_named_constant.py +98 -0
- core_10x/unit_tests/test_rc.py +11 -0
- core_10x/unit_tests/test_rdate.py +484 -0
- core_10x/unit_tests/test_trait_method_error.py +80 -0
- core_10x/unit_tests/test_trait_modification.py +19 -0
- core_10x/unit_tests/test_traitable.py +959 -0
- core_10x/unit_tests/test_traitable_history.py +1 -0
- core_10x/unit_tests/test_ts_store.py +1 -0
- core_10x/unit_tests/test_ts_union.py +369 -0
- core_10x/unit_tests/test_ui_nodes.py +81 -0
- core_10x/unit_tests/test_xxcalendar.py +471 -0
- core_10x/vault/__init__.py +0 -0
- core_10x/vault/sec_keys.py +133 -0
- core_10x/vault/security_keys_old.py +168 -0
- core_10x/vault/vault.py +56 -0
- core_10x/vault/vault_traitable.py +56 -0
- core_10x/vault/vault_user.py +70 -0
- core_10x/xdate_time.py +136 -0
- core_10x/xnone.py +71 -0
- core_10x/xxcalendar.py +228 -0
- infra_10x/__init__.py +0 -0
- infra_10x/manual_tests/__init__.py +0 -0
- infra_10x/manual_tests/test_misc.py +16 -0
- infra_10x/manual_tests/test_prepare_filter_and_pipeline.py +25 -0
- infra_10x/mongodb_admin.py +111 -0
- infra_10x/mongodb_store.py +346 -0
- infra_10x/mongodb_utils.py +129 -0
- infra_10x/unit_tests/conftest.py +13 -0
- infra_10x/unit_tests/test_mongo_db.py +36 -0
- infra_10x/unit_tests/test_mongo_history.py +1 -0
- py10x_universe-0.1.3.dist-info/METADATA +406 -0
- py10x_universe-0.1.3.dist-info/RECORD +214 -0
- py10x_universe-0.1.3.dist-info/WHEEL +4 -0
- py10x_universe-0.1.3.dist-info/licenses/LICENSE +21 -0
- ui_10x/__init__.py +0 -0
- ui_10x/apps/__init__.py +0 -0
- ui_10x/apps/collection_editor_app.py +100 -0
- ui_10x/choice.py +212 -0
- ui_10x/collection_editor.py +135 -0
- ui_10x/concrete_trait_widgets.py +220 -0
- ui_10x/conftest.py +8 -0
- ui_10x/entity_stocker.py +173 -0
- ui_10x/examples/__init__.py +0 -0
- ui_10x/examples/_guess_word_data.py +14076 -0
- ui_10x/examples/collection_editor.py +17 -0
- ui_10x/examples/date_selector.py +14 -0
- ui_10x/examples/entity_stocker.py +18 -0
- ui_10x/examples/guess_word.py +392 -0
- ui_10x/examples/message_box.py +20 -0
- ui_10x/examples/multi_choice.py +17 -0
- ui_10x/examples/py_data_browser.py +66 -0
- ui_10x/examples/radiobox.py +29 -0
- ui_10x/examples/single_choice.py +31 -0
- ui_10x/examples/style_sheet.py +47 -0
- ui_10x/examples/trivial_entity_editor.py +18 -0
- ui_10x/platform.py +20 -0
- ui_10x/platform_interface.py +517 -0
- ui_10x/py_data_browser.py +249 -0
- ui_10x/qt6/__init__.py +0 -0
- ui_10x/qt6/conftest.py +8 -0
- ui_10x/qt6/manual_tests/__init__.py +0 -0
- ui_10x/qt6/manual_tests/basic_test.py +35 -0
- ui_10x/qt6/platform_implementation.py +275 -0
- ui_10x/qt6/utils.py +665 -0
- ui_10x/rio/__init__.py +0 -0
- ui_10x/rio/apps/examples/examples/__init__.py +22 -0
- ui_10x/rio/apps/examples/examples/components/__init__.py +3 -0
- ui_10x/rio/apps/examples/examples/components/collection_editor.py +15 -0
- ui_10x/rio/apps/examples/examples/pages/collection_editor.py +21 -0
- ui_10x/rio/apps/examples/examples/pages/login_page.py +88 -0
- ui_10x/rio/apps/examples/examples/pages/style_sheet.py +21 -0
- ui_10x/rio/apps/examples/rio.toml +14 -0
- ui_10x/rio/component_builder.py +497 -0
- ui_10x/rio/components/__init__.py +9 -0
- ui_10x/rio/components/group_box.py +31 -0
- ui_10x/rio/components/labeled_checkbox.py +18 -0
- ui_10x/rio/components/line_edit.py +37 -0
- ui_10x/rio/components/radio_button.py +32 -0
- ui_10x/rio/components/separator.py +24 -0
- ui_10x/rio/components/splitter.py +121 -0
- ui_10x/rio/components/tree_view.py +75 -0
- ui_10x/rio/conftest.py +35 -0
- ui_10x/rio/internals/__init__.py +0 -0
- ui_10x/rio/internals/app.py +192 -0
- ui_10x/rio/manual_tests/__init__.py +0 -0
- ui_10x/rio/manual_tests/basic_test.py +24 -0
- ui_10x/rio/manual_tests/splitter.py +27 -0
- ui_10x/rio/platform_implementation.py +91 -0
- ui_10x/rio/style_sheet.py +53 -0
- ui_10x/rio/unit_tests/test_collection_editor.py +68 -0
- ui_10x/rio/unit_tests/test_internals.py +630 -0
- ui_10x/rio/unit_tests/test_style_sheet.py +37 -0
- ui_10x/rio/widgets/__init__.py +46 -0
- ui_10x/rio/widgets/application.py +109 -0
- ui_10x/rio/widgets/button.py +48 -0
- ui_10x/rio/widgets/button_group.py +60 -0
- ui_10x/rio/widgets/calendar.py +23 -0
- ui_10x/rio/widgets/checkbox.py +24 -0
- ui_10x/rio/widgets/dialog.py +137 -0
- ui_10x/rio/widgets/group_box.py +27 -0
- ui_10x/rio/widgets/layout.py +34 -0
- ui_10x/rio/widgets/line_edit.py +37 -0
- ui_10x/rio/widgets/list.py +105 -0
- ui_10x/rio/widgets/message_box.py +70 -0
- ui_10x/rio/widgets/scroll_area.py +31 -0
- ui_10x/rio/widgets/spacer.py +6 -0
- ui_10x/rio/widgets/splitter.py +45 -0
- ui_10x/rio/widgets/text_edit.py +28 -0
- ui_10x/rio/widgets/tree.py +89 -0
- ui_10x/rio/widgets/unit_tests/test_button.py +101 -0
- ui_10x/rio/widgets/unit_tests/test_button_group.py +33 -0
- ui_10x/rio/widgets/unit_tests/test_calendar.py +114 -0
- ui_10x/rio/widgets/unit_tests/test_checkbox.py +109 -0
- ui_10x/rio/widgets/unit_tests/test_group_box.py +158 -0
- ui_10x/rio/widgets/unit_tests/test_label.py +43 -0
- ui_10x/rio/widgets/unit_tests/test_line_edit.py +140 -0
- ui_10x/rio/widgets/unit_tests/test_list.py +146 -0
- ui_10x/table_header_view.py +305 -0
- ui_10x/table_view.py +174 -0
- ui_10x/trait_editor.py +189 -0
- ui_10x/trait_widget.py +131 -0
- ui_10x/traitable_editor.py +200 -0
- ui_10x/traitable_view.py +131 -0
- ui_10x/unit_tests/conftest.py +8 -0
- ui_10x/unit_tests/test_platform.py +9 -0
- ui_10x/utils.py +661 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import inspect
|
|
3
|
+
|
|
4
|
+
from core_10x.named_constant import EnumBits, NamedConstant
|
|
5
|
+
from core_10x.nucleus import Nucleus
|
|
6
|
+
from core_10x.package_refactoring import PackageRefactoring
|
|
7
|
+
from core_10x.py_class import PyClass
|
|
8
|
+
from core_10x.trait import T, Trait
|
|
9
|
+
from core_10x.ui_hint import Ui
|
|
10
|
+
from core_10x.xdate_time import XDateTime, date, datetime
|
|
11
|
+
from core_10x.xnone import XNone, XNoneType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class primitive_trait(Trait, register=False):
|
|
15
|
+
s_ui_hint = Ui.NONE
|
|
16
|
+
|
|
17
|
+
def default_value(self):
|
|
18
|
+
return self.default
|
|
19
|
+
|
|
20
|
+
def same_values(self, value1, value2) -> bool:
|
|
21
|
+
return value1 == value2
|
|
22
|
+
|
|
23
|
+
def from_str(self, s: str):
|
|
24
|
+
try:
|
|
25
|
+
v = ast.literal_eval(s)
|
|
26
|
+
dt = self.data_type
|
|
27
|
+
return v if type(v) is dt else dt(v)
|
|
28
|
+
except Exception:
|
|
29
|
+
return XNone
|
|
30
|
+
|
|
31
|
+
def from_any_xstr(self, value):
|
|
32
|
+
return self.data_type(value)
|
|
33
|
+
|
|
34
|
+
# def to_str(self, v) -> str:
|
|
35
|
+
# return str(v)
|
|
36
|
+
|
|
37
|
+
def to_id(self, v) -> str:
|
|
38
|
+
return str(v)
|
|
39
|
+
|
|
40
|
+
def serialize(self, value):
|
|
41
|
+
return value
|
|
42
|
+
|
|
43
|
+
def deserialize(self, value):
|
|
44
|
+
return value
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class bool_trait(primitive_trait, data_type=bool):
|
|
48
|
+
s_ui_hint = Ui.check()
|
|
49
|
+
|
|
50
|
+
# fmt: off
|
|
51
|
+
FORMATS = [
|
|
52
|
+
('yes',''),
|
|
53
|
+
('yes','no'),
|
|
54
|
+
('true','false'),
|
|
55
|
+
('on','off')
|
|
56
|
+
]
|
|
57
|
+
fmt = FORMATS[0]
|
|
58
|
+
# fmt: on
|
|
59
|
+
|
|
60
|
+
def to_id(self, value: bool) -> str:
|
|
61
|
+
return '0' if value else '1'
|
|
62
|
+
|
|
63
|
+
def from_str(self, s: str):
|
|
64
|
+
res = super().from_str(s)
|
|
65
|
+
if res is not XNone:
|
|
66
|
+
return res
|
|
67
|
+
s = s.strip().lower()
|
|
68
|
+
return next((f[0] == s for f in self.FORMATS if s in f), XNone)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class int_trait(primitive_trait, data_type=int):
|
|
72
|
+
s_ui_hint = Ui.line()
|
|
73
|
+
|
|
74
|
+
fmt = ','
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class float_trait(primitive_trait, data_type=float):
|
|
78
|
+
s_ui_hint = Ui.line()
|
|
79
|
+
|
|
80
|
+
fmt = ',.2f'
|
|
81
|
+
|
|
82
|
+
# def from_str(self, s: str) -> RC:
|
|
83
|
+
# return RC(True, locale.atof(s))
|
|
84
|
+
|
|
85
|
+
def is_acceptable_type(self, data_type: type) -> bool:
|
|
86
|
+
return data_type is float or data_type is int
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class str_trait(primitive_trait, data_type=str):
|
|
90
|
+
s_ui_hint = Ui.line(align_h=-1)
|
|
91
|
+
|
|
92
|
+
# pattern = ''
|
|
93
|
+
# placeholder = ''
|
|
94
|
+
|
|
95
|
+
def from_str(self, s: str):
|
|
96
|
+
return s
|
|
97
|
+
|
|
98
|
+
def to_str(self, v) -> str:
|
|
99
|
+
return v
|
|
100
|
+
|
|
101
|
+
def to_id(self, value: str) -> str:
|
|
102
|
+
return value
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# class bin_trait(Trait, data_type = bytes):
|
|
106
|
+
# w = 0 #-- max width, if any and relevant
|
|
107
|
+
# h = 0 #-- max height, if any and relevant
|
|
108
|
+
#
|
|
109
|
+
# def pixmap(*args, **kwargs):
|
|
110
|
+
# tdef: TraitDefinition = T(bytes, *args, **kwargs)
|
|
111
|
+
# tdef.set_widget_type(Ui.WIDGET_TYPE.PIXMAP)
|
|
112
|
+
# return tdef
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class datetime_trait(Trait, data_type=datetime):
|
|
116
|
+
s_ui_hint = Ui.line(align_h=0)
|
|
117
|
+
|
|
118
|
+
def from_str(self, s: str):
|
|
119
|
+
dt = XDateTime.str_to_datetime(s)
|
|
120
|
+
if dt is None:
|
|
121
|
+
return XNone
|
|
122
|
+
return dt
|
|
123
|
+
|
|
124
|
+
def from_any_xstr(self, value):
|
|
125
|
+
dt = XDateTime.to_datetime(value)
|
|
126
|
+
if dt is None:
|
|
127
|
+
return XNone
|
|
128
|
+
return dt
|
|
129
|
+
|
|
130
|
+
def to_str(self, v: datetime) -> str:
|
|
131
|
+
return XDateTime.datetime_to_str(v)
|
|
132
|
+
|
|
133
|
+
# s_acceptable_types = { datetime, date, int, str }
|
|
134
|
+
# def is_acceptable_type(self, data_type: type) -> bool:
|
|
135
|
+
# return data_type in self.s_acceptable_types
|
|
136
|
+
|
|
137
|
+
# -- NOTES:
|
|
138
|
+
# -- 1) we believe datetime is mostly acceptable for a storage, e.g., Mongo
|
|
139
|
+
# -- 2) it is in UTC, so no locale's datetime issues
|
|
140
|
+
|
|
141
|
+
def serialize(self, value: datetime):
|
|
142
|
+
return value
|
|
143
|
+
|
|
144
|
+
def deserialize(self, value: datetime):
|
|
145
|
+
return value
|
|
146
|
+
|
|
147
|
+
def to_id(self, value) -> str:
|
|
148
|
+
return XDateTime.datetime_to_str(value, with_ms=True)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class date_trait(Trait, data_type=date):
|
|
152
|
+
s_ui_hint = Ui.line(min_width=10, align_h=0)
|
|
153
|
+
|
|
154
|
+
def from_str(self, s: str):
|
|
155
|
+
dt = XDateTime.str_to_date(s)
|
|
156
|
+
if dt is None:
|
|
157
|
+
return XNone
|
|
158
|
+
return dt
|
|
159
|
+
|
|
160
|
+
def from_any_xstr(self, value):
|
|
161
|
+
dt = XDateTime.to_date(value)
|
|
162
|
+
if dt is None:
|
|
163
|
+
return XNone
|
|
164
|
+
return dt
|
|
165
|
+
|
|
166
|
+
def to_str(self, v: date) -> str:
|
|
167
|
+
return XDateTime.date_to_str(v)
|
|
168
|
+
|
|
169
|
+
# s_acceptable_types = { datetime, date, int, str }
|
|
170
|
+
# def is_acceptable_type(self, data_type: type) -> bool:
|
|
171
|
+
# return data_type in self.s_acceptable_types
|
|
172
|
+
|
|
173
|
+
def serialize(self, value: date):
|
|
174
|
+
return XDateTime.date_to_str(value, format=XDateTime.FORMAT_X10)
|
|
175
|
+
|
|
176
|
+
def deserialize(self, value):
|
|
177
|
+
return XDateTime.str_to_date(value, format=XDateTime.FORMAT_X10)
|
|
178
|
+
|
|
179
|
+
def to_id(self, value) -> str:
|
|
180
|
+
return XDateTime.date_to_str(value)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class bytes_trait(Trait, data_type=bytes):
|
|
184
|
+
s_ui_hint = Ui.NONE
|
|
185
|
+
|
|
186
|
+
def to_str(self, value):
|
|
187
|
+
return value.decode(encoding='utf-8')
|
|
188
|
+
|
|
189
|
+
def to_id(self, value) -> str:
|
|
190
|
+
return self.to_str(value)
|
|
191
|
+
|
|
192
|
+
def same_values(self, value1, value2) -> bool:
|
|
193
|
+
return value1 == value2
|
|
194
|
+
|
|
195
|
+
def from_str(self, s: str):
|
|
196
|
+
return bytes(s, encoding='utf-8')
|
|
197
|
+
|
|
198
|
+
def from_any_xstr(self, value):
|
|
199
|
+
return XNone # may not be called!
|
|
200
|
+
|
|
201
|
+
# def is_acceptable_type(self, data_type: type) -> bool:
|
|
202
|
+
# ...
|
|
203
|
+
|
|
204
|
+
def serialize(self, value):
|
|
205
|
+
return value
|
|
206
|
+
|
|
207
|
+
def deserialize(self, value):
|
|
208
|
+
return value
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class class_trait(Trait, data_type=type):
|
|
212
|
+
s_ui_hint = Ui.line(align_h=-1)
|
|
213
|
+
|
|
214
|
+
def to_str(self, value):
|
|
215
|
+
return PyClass.name(value)
|
|
216
|
+
|
|
217
|
+
def to_id(self, value) -> str:
|
|
218
|
+
return PackageRefactoring.find_class_id(value)
|
|
219
|
+
|
|
220
|
+
def same_values(self, value1, value2) -> bool:
|
|
221
|
+
return value1 is value2
|
|
222
|
+
|
|
223
|
+
def from_str(self, s: str):
|
|
224
|
+
return PackageRefactoring.find_class(s)
|
|
225
|
+
|
|
226
|
+
def from_any_xstr(self, value):
|
|
227
|
+
return XNone # may not be called!
|
|
228
|
+
|
|
229
|
+
def is_acceptable_type(self, data_type: type) -> bool:
|
|
230
|
+
return inspect.isclass(data_type)
|
|
231
|
+
|
|
232
|
+
def serialize(self, value):
|
|
233
|
+
return PackageRefactoring.find_class_id(value)
|
|
234
|
+
|
|
235
|
+
def deserialize(self, value: str):
|
|
236
|
+
return PackageRefactoring.find_class(value)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class list_trait(Trait, data_type=list):
|
|
240
|
+
s_ui_hint = Ui.line()
|
|
241
|
+
|
|
242
|
+
def to_str(self, v) -> str:
|
|
243
|
+
return str(v)
|
|
244
|
+
|
|
245
|
+
def to_id(self, value) -> str:
|
|
246
|
+
return str(value)
|
|
247
|
+
|
|
248
|
+
def default_value(self) -> list:
|
|
249
|
+
return list(self.default)
|
|
250
|
+
|
|
251
|
+
def serialize(self, value: list):
|
|
252
|
+
return Nucleus.serialize_list(value, self.flags_on(T.EMBEDDED))
|
|
253
|
+
|
|
254
|
+
def deserialize(self, value: list):
|
|
255
|
+
return Nucleus.deserialize_list(value)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class dict_trait(Trait, data_type=dict):
|
|
259
|
+
s_ui_hint = Ui.line(flags=Ui.SELECT_ONLY)
|
|
260
|
+
|
|
261
|
+
def use_format_str(self, fmt: str, value) -> str:
|
|
262
|
+
return str(value) if not fmt else fmt.join(f"'{key}' -> '{val}'" for key, val in value.items())
|
|
263
|
+
|
|
264
|
+
def to_str(self, v) -> str:
|
|
265
|
+
return str(v)
|
|
266
|
+
|
|
267
|
+
def to_id(self, value) -> str:
|
|
268
|
+
return str(value)
|
|
269
|
+
|
|
270
|
+
def default_value(self) -> dict:
|
|
271
|
+
return dict(self.default)
|
|
272
|
+
|
|
273
|
+
def serialize(self, value: dict):
|
|
274
|
+
return Nucleus.serialize_dict(value, self.flags_on(T.EMBEDDED))
|
|
275
|
+
|
|
276
|
+
def deserialize(self, value: dict):
|
|
277
|
+
return Nucleus.deserialize_dict(value)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class any_trait(Trait, data_type=XNoneType): # -- any
|
|
281
|
+
s_ui_hint = Ui.NONE
|
|
282
|
+
|
|
283
|
+
def to_str(self, v) -> str:
|
|
284
|
+
return str(v)
|
|
285
|
+
|
|
286
|
+
def to_id(self, value) -> str:
|
|
287
|
+
return str(value)
|
|
288
|
+
|
|
289
|
+
def from_str(self, s: str):
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
def from_any_xstr(self, value):
|
|
293
|
+
return value
|
|
294
|
+
|
|
295
|
+
def is_acceptable_type(self, data_type: type) -> bool:
|
|
296
|
+
return True
|
|
297
|
+
|
|
298
|
+
def serialize(self, value):
|
|
299
|
+
return Nucleus.serialize_any(value, self.flags_on(T.EMBEDDED))
|
|
300
|
+
|
|
301
|
+
def deserialize(self, value):
|
|
302
|
+
return Nucleus.deserialize_any(value, None) # expected class unknown
|
|
303
|
+
|
|
304
|
+
def same_values(self, value1, value2) -> bool:
|
|
305
|
+
return value1 is value2
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class nucleus_trait(Trait, data_type=Nucleus, base_class=True):
|
|
309
|
+
s_ui_hint = Ui.NONE
|
|
310
|
+
|
|
311
|
+
def to_str(self, v) -> str:
|
|
312
|
+
return self.data_type.to_str(v)
|
|
313
|
+
|
|
314
|
+
def to_id(self, v) -> str:
|
|
315
|
+
return self.data_type.to_id(v)
|
|
316
|
+
|
|
317
|
+
def from_str(self, s: str) -> Nucleus:
|
|
318
|
+
return self.data_type.from_str(s)
|
|
319
|
+
|
|
320
|
+
def from_any_xstr(self, value) -> Nucleus:
|
|
321
|
+
return self.data_type.from_any_xstr(value)
|
|
322
|
+
|
|
323
|
+
def is_acceptable_type(self, data_type: type) -> bool:
|
|
324
|
+
return issubclass(data_type, self.data_type)
|
|
325
|
+
|
|
326
|
+
def same_values(self, value1, value2) -> bool:
|
|
327
|
+
return self.data_type.same_values(value1, value2)
|
|
328
|
+
|
|
329
|
+
def serialize(self, value: Nucleus):
|
|
330
|
+
if type(value) is self.data_type:
|
|
331
|
+
return value.serialize(self.flags_on(T.EMBEDDED))
|
|
332
|
+
|
|
333
|
+
return Nucleus.serialize_any(value, self.flags_on(T.EMBEDDED))
|
|
334
|
+
|
|
335
|
+
def deserialize(self, serialized_value) -> Nucleus:
|
|
336
|
+
if isinstance(serialized_value, dict) and (value := Nucleus.deserialize_record(serialized_value)):
|
|
337
|
+
return value
|
|
338
|
+
|
|
339
|
+
return self.data_type.deserialize(serialized_value)
|
|
340
|
+
|
|
341
|
+
def choices(self):
|
|
342
|
+
return self.data_type.choose_from()
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class named_constant_trait(nucleus_trait, data_type=NamedConstant, base_class=True):
|
|
346
|
+
s_ui_hint = Ui.choice(flags=Ui.SELECT_ONLY)
|
|
347
|
+
|
|
348
|
+
def is_acceptable_type(self, data_type: type) -> bool:
|
|
349
|
+
return data_type is self.data_type
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class flags_trait(nucleus_trait, data_type=EnumBits, base_class=True):
|
|
353
|
+
s_ui_hint = Ui.line()
|
|
354
|
+
|
|
355
|
+
def is_acceptable_type(self, data_type: type) -> bool:
|
|
356
|
+
return data_type is self.data_type
|
core_10x/conftest.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from core_10x.ts_store import TsStore
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.fixture(scope='module')
|
|
7
|
+
def ts_instance():
|
|
8
|
+
from core_10x.testlib.test_store import TestStore
|
|
9
|
+
|
|
10
|
+
assert not TsStore.s_instances
|
|
11
|
+
yield TestStore.instance()
|
|
12
|
+
TsStore.s_instances.clear()
|
core_10x/curve.py
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import bisect
|
|
4
|
+
import math
|
|
5
|
+
from datetime import date, timedelta
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from scipy import interpolate
|
|
9
|
+
|
|
10
|
+
from core_10x.named_constant import NamedConstant
|
|
11
|
+
from core_10x.traitable import RC, RC_TRUE, RT, AnonymousTraitable, M, T, Traitable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class IP_KIND(NamedConstant, lowercase_values=True):
|
|
15
|
+
# fmt: off
|
|
16
|
+
#-- NOTE: enum labels specify the minimum number of points required!
|
|
17
|
+
LINEAR = ( 2, )
|
|
18
|
+
NEAREST = ( 2, )
|
|
19
|
+
NEAREST_UP = ( 2, )
|
|
20
|
+
PREVIOUS = ( 2, ) #-- return the previous value of the point
|
|
21
|
+
NEXT = ( 2, ) #-- ------ the next ----
|
|
22
|
+
ZERO = ( 1, ) #-- spline interp of zeroth order
|
|
23
|
+
SLINEAR = ( 2, ) #-- spline interp of first order
|
|
24
|
+
QUADRATIC = ( 3, ) #-- spline interp of second order
|
|
25
|
+
CUBIC = ( 4, ) #-- spline interp of third order
|
|
26
|
+
|
|
27
|
+
NO_INTERP = ( 0, ) #-- NO interp outside of given nodes
|
|
28
|
+
# fmt: on
|
|
29
|
+
|
|
30
|
+
# @classmethod
|
|
31
|
+
# def labelFromValue( cls, value ) -> int:
|
|
32
|
+
# return int( super().labelFromValue( value ) )
|
|
33
|
+
#
|
|
34
|
+
# @classmethod
|
|
35
|
+
# def defaultValueForName( cls, name: str ) -> str:
|
|
36
|
+
# return name.replace( '_', '-' ).lower()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# -- TODO: it must be derived from AnonymousTraitable. Had to make it RT and derive from Traitable due to a bug - trying to load the embedded portion from its own collection
|
|
40
|
+
class CurveParams(Traitable):
|
|
41
|
+
# fmt: off
|
|
42
|
+
DEFAULT_INTERPOLATOR = interpolate.interp1d
|
|
43
|
+
|
|
44
|
+
interpolator: Any = RT(default = DEFAULT_INTERPOLATOR)
|
|
45
|
+
ip_kind: IP_KIND = RT(IP_KIND.LINEAR)
|
|
46
|
+
assume_sorted: bool = RT(True)
|
|
47
|
+
copy: bool = RT(False)
|
|
48
|
+
fill_value: str = RT('extrapolate')
|
|
49
|
+
bounds_error: bool = RT(False)
|
|
50
|
+
# fmt: on
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Curve(AnonymousTraitable):
|
|
54
|
+
# fmt: off
|
|
55
|
+
times: list = T([]) #-- only ints or floats are allowed
|
|
56
|
+
values: list = T([])
|
|
57
|
+
params: CurveParams = RT() #-- TODO: must be T(T.EMBEDDED)
|
|
58
|
+
|
|
59
|
+
beginning_of_time: Any = T(None) #-- may be float or int
|
|
60
|
+
|
|
61
|
+
interpolator: Any = RT()
|
|
62
|
+
min_curve_size: int = RT()
|
|
63
|
+
# fmt: on
|
|
64
|
+
|
|
65
|
+
def params_get(self) -> CurveParams:
|
|
66
|
+
return CurveParams()
|
|
67
|
+
|
|
68
|
+
def min_curve_size_get(self) -> int:
|
|
69
|
+
return self.params.ip_kind.label
|
|
70
|
+
|
|
71
|
+
def start_time(self):
|
|
72
|
+
times = self.times
|
|
73
|
+
return times[0] if times else None
|
|
74
|
+
|
|
75
|
+
def end_time(self):
|
|
76
|
+
times = self.times
|
|
77
|
+
return times[-1] if times else None
|
|
78
|
+
|
|
79
|
+
def update(self, t, value, reset=True):
|
|
80
|
+
if type(value) is not float: # -- TODO: we sometimes have np.floats
|
|
81
|
+
value = float(value)
|
|
82
|
+
|
|
83
|
+
times = self.times
|
|
84
|
+
values = self.values
|
|
85
|
+
i = bisect.bisect_left(times, t)
|
|
86
|
+
if i >= len(times):
|
|
87
|
+
times.append(t)
|
|
88
|
+
values.append(value)
|
|
89
|
+
else:
|
|
90
|
+
if times[i] == t:
|
|
91
|
+
values[i] = value
|
|
92
|
+
else:
|
|
93
|
+
times.insert(i, t)
|
|
94
|
+
values.insert(i, value)
|
|
95
|
+
|
|
96
|
+
self.times = times
|
|
97
|
+
self.values = values
|
|
98
|
+
if reset:
|
|
99
|
+
self.reset()
|
|
100
|
+
|
|
101
|
+
def update_many(self, times, values, reset=True):
|
|
102
|
+
assert len(times) == len(values), 'times and values size mismatch'
|
|
103
|
+
for i, t in enumerate(times):
|
|
104
|
+
self.update(t, values[i], reset=False)
|
|
105
|
+
|
|
106
|
+
if reset:
|
|
107
|
+
self.reset()
|
|
108
|
+
|
|
109
|
+
def remove(self, t, reset=True) -> bool:
|
|
110
|
+
times = self.times
|
|
111
|
+
if t not in times:
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
values = self.values
|
|
115
|
+
i = times.index(t)
|
|
116
|
+
times.pop(i)
|
|
117
|
+
values.pop(i)
|
|
118
|
+
if reset:
|
|
119
|
+
self.reset()
|
|
120
|
+
|
|
121
|
+
return True
|
|
122
|
+
|
|
123
|
+
def perturb(self, t, new_value, perturb_existing_only=False):
|
|
124
|
+
if perturb_existing_only:
|
|
125
|
+
assert t in self.times, f't = {t} is not in the curve'
|
|
126
|
+
|
|
127
|
+
values = self.values
|
|
128
|
+
self.invalidate_value('values')
|
|
129
|
+
self.values = values
|
|
130
|
+
self.update(t, new_value)
|
|
131
|
+
|
|
132
|
+
def perturb_shift(self, t, value_shift, perturb_existing_only=False):
|
|
133
|
+
self.perturb(t, self.value(t) + value_shift, perturb_existing_only)
|
|
134
|
+
|
|
135
|
+
def perturb_proportional(self, t, value_shift_mult, perturb_existing_only=False):
|
|
136
|
+
self.perturb(t, self.value(t) * value_shift_mult, perturb_existing_only)
|
|
137
|
+
|
|
138
|
+
def interpolator_get(self):
|
|
139
|
+
params = self.params
|
|
140
|
+
# fmt: off
|
|
141
|
+
return params.interpolator(
|
|
142
|
+
self.times, self.values,
|
|
143
|
+
kind = params.ip_kind.value,
|
|
144
|
+
assume_sorted = params.assume_sorted,
|
|
145
|
+
copy = params.copy,
|
|
146
|
+
fill_value = params.fill_value,
|
|
147
|
+
bounds_error = params.bounds_error,
|
|
148
|
+
)
|
|
149
|
+
# fmt: on
|
|
150
|
+
|
|
151
|
+
def value(self, t) -> float:
|
|
152
|
+
times = self.times
|
|
153
|
+
|
|
154
|
+
if self.params.ip_kind is IP_KIND.NO_INTERP:
|
|
155
|
+
return self.values[times.index(t)] if t in times else math.nan
|
|
156
|
+
|
|
157
|
+
if len(times) < self.min_curve_size:
|
|
158
|
+
if t in times:
|
|
159
|
+
return self.values[times.index(t)]
|
|
160
|
+
|
|
161
|
+
bot = self.beginning_of_time
|
|
162
|
+
return float(self.interpolator(t)) if (bot is not None) or (t >= bot) else math.nan
|
|
163
|
+
|
|
164
|
+
def values_at(self, dates) -> tuple:
|
|
165
|
+
return tuple(self.value(d) for d in dates)
|
|
166
|
+
|
|
167
|
+
def reset(self):
|
|
168
|
+
self.invalidate_value('interpolator')
|
|
169
|
+
|
|
170
|
+
# @classmethod
|
|
171
|
+
# def _uniqueTimesValues(cls, times: list, values: list, keep_last_update: bool) -> tuple:
|
|
172
|
+
# assert len(times) == len(values), f'{len(times)} != {len(values)}: sizes of times and values must be equal'
|
|
173
|
+
#
|
|
174
|
+
# last_t = times[0]
|
|
175
|
+
# last_i = 0
|
|
176
|
+
# times_unique = [last_t]
|
|
177
|
+
# values_unique = [values[0]]
|
|
178
|
+
#
|
|
179
|
+
# for t, v in zip(times, values, strict=True):
|
|
180
|
+
# assert t >= last_t, f'times must be a non-decreasing list, but {t} < {last_t}'
|
|
181
|
+
#
|
|
182
|
+
# if t > last_t:
|
|
183
|
+
# last_t = t
|
|
184
|
+
# last_i += 1
|
|
185
|
+
# times_unique.append(t)
|
|
186
|
+
# values_unique.append(v)
|
|
187
|
+
# else:
|
|
188
|
+
# if keep_last_update:
|
|
189
|
+
# values_unique[last_i] = v
|
|
190
|
+
#
|
|
191
|
+
# return (times_unique, values_unique)
|
|
192
|
+
#
|
|
193
|
+
# def uniquePointsCurve(self, keep_last_update=True, copy_curve=False) -> Curve:
|
|
194
|
+
# times_unique, values_unique = self._uniqueTimesValues(self.times(), self.values(), keep_last_update)
|
|
195
|
+
# if copy_curve:
|
|
196
|
+
# return self.clone(times=times_unique, values=values_unique)
|
|
197
|
+
#
|
|
198
|
+
# self.times = times_unique
|
|
199
|
+
# self.values = values_unique
|
|
200
|
+
# self.reset()
|
|
201
|
+
# return self
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class TwoFuncInterpolator:
|
|
205
|
+
def __init__(self, in_func, out_func, in_func_on_arrays=None, _interpolator=interpolate.interp1d):
|
|
206
|
+
if in_func:
|
|
207
|
+
in_func_on_arrays = lambda list_x, list_y: [in_func(x, list_y[i]) for i, x in enumerate(list_x)]
|
|
208
|
+
|
|
209
|
+
assert in_func_on_arrays, 'either in_func or in_func_on_arrays must be provided'
|
|
210
|
+
|
|
211
|
+
self.in_func = in_func_on_arrays
|
|
212
|
+
self.out_func = out_func
|
|
213
|
+
self.interpolator = _interpolator
|
|
214
|
+
|
|
215
|
+
def __call__(self, x, y, **kwargs):
|
|
216
|
+
values = self.in_func(x, y)
|
|
217
|
+
ip_method = self.interpolator(x, values, **kwargs)
|
|
218
|
+
out_func = self.out_func
|
|
219
|
+
|
|
220
|
+
def _method(t):
|
|
221
|
+
v = ip_method(t)
|
|
222
|
+
return out_func(t, v)
|
|
223
|
+
|
|
224
|
+
return _method
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class DateCurve(Curve):
|
|
228
|
+
beginning_of_time: int = M() # -- TODO: looks like Any trait fails to get deserialized - bug
|
|
229
|
+
dates: list = RT()
|
|
230
|
+
|
|
231
|
+
s_epoch_date = date(1970, 1, 1)
|
|
232
|
+
|
|
233
|
+
@classmethod
|
|
234
|
+
def _to_number(cls, d) -> int:
|
|
235
|
+
if isinstance(d, date):
|
|
236
|
+
return (d - cls.s_epoch_date).days
|
|
237
|
+
elif isinstance(d, int):
|
|
238
|
+
return d
|
|
239
|
+
|
|
240
|
+
raise ValueError(f'Unexpected value {d}')
|
|
241
|
+
|
|
242
|
+
@classmethod
|
|
243
|
+
def _from_number(cls, x: int) -> date:
|
|
244
|
+
return cls.s_epoch_date + timedelta(days=x)
|
|
245
|
+
|
|
246
|
+
def dates_get(self) -> list:
|
|
247
|
+
f = self._from_number
|
|
248
|
+
return [f(x) for x in self.times]
|
|
249
|
+
|
|
250
|
+
def dates_set(self, trait, value) -> RC:
|
|
251
|
+
f = self._to_number # -- TODO: possibly improve performance by using a different f (which doesn't check the type)
|
|
252
|
+
times = [f(d) for d in value]
|
|
253
|
+
return self.set_value('times', times)
|
|
254
|
+
|
|
255
|
+
def beginning_of_time_set(self, trait, d) -> RC:
|
|
256
|
+
t = self._to_number(d)
|
|
257
|
+
self.raw_set_value(trait, t)
|
|
258
|
+
return RC_TRUE
|
|
259
|
+
|
|
260
|
+
def beginning_of_time_as_date(self) -> date:
|
|
261
|
+
return self._from_number(self.beginning_of_time)
|
|
262
|
+
|
|
263
|
+
def start_time(self) -> date:
|
|
264
|
+
times = self.dates
|
|
265
|
+
return times[0] if times else None
|
|
266
|
+
|
|
267
|
+
def end_time(self) -> date:
|
|
268
|
+
times = self.dates
|
|
269
|
+
return times[-1] if times else None
|
|
270
|
+
|
|
271
|
+
def update(self, d: date, value, reset=True):
|
|
272
|
+
x = self._to_number(d)
|
|
273
|
+
super().update(x, value, reset=reset)
|
|
274
|
+
|
|
275
|
+
def reset(self):
|
|
276
|
+
super().reset()
|
|
277
|
+
self.invalidate_value('dates')
|
|
278
|
+
|
|
279
|
+
def value(self, d: date) -> float:
|
|
280
|
+
x = self._to_number(d)
|
|
281
|
+
return super().value(x)
|
|
282
|
+
|
|
283
|
+
def remove(self, d, reset=True):
|
|
284
|
+
t = self._to_number(d)
|
|
285
|
+
if super().remove(t, reset=reset):
|
|
286
|
+
self.invalidate_value('dates')
|
|
287
|
+
|
|
288
|
+
def perturb(self, d: date, new_value, perturb_existing_only=False):
|
|
289
|
+
if perturb_existing_only:
|
|
290
|
+
assert d in self.dates, f'{d} is not in the curve'
|
|
291
|
+
|
|
292
|
+
values = self.values
|
|
293
|
+
self.invalidate_value('values')
|
|
294
|
+
self.values = values
|
|
295
|
+
self.update(d, new_value)
|
|
296
|
+
|
|
297
|
+
# def bracketDateNodes(self, d: date) -> tuple:
|
|
298
|
+
# t = self._to_number(d)
|
|
299
|
+
#
|
|
300
|
+
# times: list = self.times
|
|
301
|
+
# last_time = times[-1]
|
|
302
|
+
# first_time = times[0]
|
|
303
|
+
# if t > last_time:
|
|
304
|
+
# return (self._from_number(last_time), None)
|
|
305
|
+
#
|
|
306
|
+
# if t < first_time:
|
|
307
|
+
# return (None, self._from_number(first_time))
|
|
308
|
+
#
|
|
309
|
+
# i = bisect.bisect_left(times, t)
|
|
310
|
+
# if times[i] == t:
|
|
311
|
+
# d = self._from_number(times[i])
|
|
312
|
+
# return (d, d)
|
|
313
|
+
#
|
|
314
|
+
# d_left = self._from_number(times[i - 1])
|
|
315
|
+
# d_right = self._from_number(times[i])
|
|
316
|
+
# return (d_left, d_right)
|
|
317
|
+
|
|
318
|
+
def dates_values(self, min_date: date = None, max_date: date = None) -> list:
|
|
319
|
+
dates = self.dates
|
|
320
|
+
values = self.values
|
|
321
|
+
return [(d, v) for d, v in zip(dates, values, strict=True) if (not min_date or d >= min_date) and (not max_date or d <= max_date)]
|