sapiopycommons 2025.2.25a450__py3-none-any.whl → 2025.3.6a451__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.
Potentially problematic release.
This version of sapiopycommons might be problematic. Click here for more details.
- sapiopycommons/ai/tool_of_tools.py +49 -91
- sapiopycommons/callbacks/callback_util.py +3 -3
- sapiopycommons/customreport/auto_pagers.py +25 -17
- sapiopycommons/files/file_writer.py +1 -1
- sapiopycommons/general/aliases.py +12 -5
- sapiopycommons/general/html_formatter.py +456 -0
- sapiopycommons/general/popup_util.py +2 -2
- sapiopycommons/general/sapio_links.py +12 -4
- sapiopycommons/recordmodel/record_handler.py +137 -101
- {sapiopycommons-2025.2.25a450.dist-info → sapiopycommons-2025.3.6a451.dist-info}/METADATA +1 -1
- {sapiopycommons-2025.2.25a450.dist-info → sapiopycommons-2025.3.6a451.dist-info}/RECORD +13 -12
- {sapiopycommons-2025.2.25a450.dist-info → sapiopycommons-2025.3.6a451.dist-info}/WHEEL +0 -0
- {sapiopycommons-2025.2.25a450.dist-info → sapiopycommons-2025.3.6a451.dist-info}/licenses/LICENSE +0 -0
|
@@ -70,56 +70,66 @@ class RecordHandler:
|
|
|
70
70
|
self.rel_man = self.rec_man.relationship_manager
|
|
71
71
|
self.an_man = RecordModelAncestorManager(self.rec_man)
|
|
72
72
|
|
|
73
|
-
def wrap_model(self, record: DataRecord, wrapper_type: type[WrappedType])
|
|
73
|
+
def wrap_model(self, record: DataRecord, wrapper_type: type[WrappedType] | None = None) \
|
|
74
|
+
-> WrappedType | PyRecordModel:
|
|
74
75
|
"""
|
|
75
76
|
Shorthand for adding a single data record as a record model.
|
|
76
77
|
|
|
77
78
|
:param record: The data record to wrap.
|
|
78
|
-
:param wrapper_type: The record model wrapper to use.
|
|
79
|
+
:param wrapper_type: The record model wrapper to use. If not provided, the record is returned as a
|
|
80
|
+
PyRecordModel instead of WrappedRecordModels.
|
|
79
81
|
:return: The record model for the input.
|
|
80
82
|
"""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
if wrapper_type is not None:
|
|
84
|
+
self.__verify_data_type([record], wrapper_type)
|
|
85
|
+
return self.inst_man.add_existing_record_of_type(record, wrapper_type)
|
|
86
|
+
return self.inst_man.add_existing_record(record)
|
|
83
87
|
|
|
84
|
-
def wrap_models(self, records: Iterable[DataRecord], wrapper_type: type[WrappedType])
|
|
88
|
+
def wrap_models(self, records: Iterable[DataRecord], wrapper_type: type[WrappedType] | None = None) \
|
|
89
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
85
90
|
"""
|
|
86
91
|
Shorthand for adding a list of data records as record models.
|
|
87
92
|
|
|
88
93
|
:param records: The data records to wrap.
|
|
89
|
-
:param wrapper_type: The record model wrapper to use.
|
|
94
|
+
:param wrapper_type: The record model wrapper to use. If not provided, the records are returned as
|
|
95
|
+
PyRecordModels instead of WrappedRecordModels.
|
|
90
96
|
:return: The record models for the input.
|
|
91
97
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
if wrapper_type is not None:
|
|
99
|
+
self.__verify_data_type(records, wrapper_type)
|
|
100
|
+
return self.inst_man.add_existing_records_of_type(list(records), wrapper_type)
|
|
101
|
+
return self.inst_man.add_existing_records(list(records))
|
|
94
102
|
|
|
95
|
-
def query_models(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
|
|
96
|
-
page_limit: int | None = None, page_size: int | None = None)
|
|
103
|
+
def query_models(self, wrapper_type: type[WrappedType] | str, field: FieldIdentifier,
|
|
104
|
+
value_list: Iterable[FieldValue], page_limit: int | None = None, page_size: int | None = None) \
|
|
105
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
97
106
|
"""
|
|
98
107
|
Shorthand for using the data record manager to query for a list of data records by field value
|
|
99
108
|
and then converting the results into a list of record models.
|
|
100
109
|
|
|
101
|
-
:param wrapper_type: The record model wrapper to use.
|
|
110
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
102
111
|
:param field: The field to query on.
|
|
103
112
|
:param value_list: The values of the field to query on.
|
|
104
113
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
105
114
|
only functions if you set a page size or the platform enforces a page size.
|
|
106
115
|
:param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
|
|
107
|
-
:return: The record models for the queried records.
|
|
116
|
+
:return: The record models for the queried records. If a data type name was used instead of a model wrapper,
|
|
117
|
+
then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
108
118
|
"""
|
|
109
119
|
criteria: DataRecordPojoPageCriteria | None = None
|
|
110
120
|
if page_size is not None:
|
|
111
121
|
criteria = DataRecordPojoPageCriteria(page_size=page_size)
|
|
112
122
|
return self.query_models_with_criteria(wrapper_type, field, value_list, criteria, page_limit)[0]
|
|
113
123
|
|
|
114
|
-
def query_and_map_models(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
|
|
124
|
+
def query_and_map_models(self, wrapper_type: type[WrappedType] | str, field: FieldIdentifier,
|
|
115
125
|
value_list: Iterable[FieldValue], page_limit: int | None = None,
|
|
116
126
|
page_size: int | None = None, *, mapping_field: FieldIdentifier | None = None) \
|
|
117
|
-
-> dict[FieldValue, list[WrappedType]]:
|
|
127
|
+
-> dict[FieldValue, list[WrappedType] | list[PyRecordModel]]:
|
|
118
128
|
"""
|
|
119
129
|
Shorthand for using query_models to search for records given values on a specific field and then using
|
|
120
130
|
map_by_field to turn the returned list into a dictionary mapping field values to records.
|
|
121
131
|
|
|
122
|
-
:param wrapper_type: The record model wrapper to use.
|
|
132
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
123
133
|
:param field: The field to query and map on.
|
|
124
134
|
:param value_list: The values of the field to query on.
|
|
125
135
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
@@ -127,22 +137,24 @@ class RecordHandler:
|
|
|
127
137
|
:param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
|
|
128
138
|
:param mapping_field: If provided, use this field to map against instead of the field that was queried on.
|
|
129
139
|
:return: The record models for the queried records mapped by field values to the records with that value.
|
|
140
|
+
If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
|
|
141
|
+
instead of WrappedRecordModels.
|
|
130
142
|
"""
|
|
131
143
|
if mapping_field is None:
|
|
132
144
|
mapping_field = field
|
|
133
145
|
return self.map_by_field(self.query_models(wrapper_type, field, value_list, page_limit, page_size),
|
|
134
146
|
mapping_field)
|
|
135
147
|
|
|
136
|
-
def query_and_unique_map_models(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
|
|
148
|
+
def query_and_unique_map_models(self, wrapper_type: type[WrappedType] | str, field: FieldIdentifier,
|
|
137
149
|
value_list: Iterable[FieldValue], page_limit: int | None = None,
|
|
138
150
|
page_size: int | None = None, *, mapping_field: FieldIdentifier | None = None) \
|
|
139
|
-
-> dict[FieldValue, WrappedType]:
|
|
151
|
+
-> dict[FieldValue, WrappedType | PyRecordModel]:
|
|
140
152
|
"""
|
|
141
153
|
Shorthand for using query_models to search for records given values on a specific field and then using
|
|
142
154
|
map_by_unique_field to turn the returned list into a dictionary mapping field values to records.
|
|
143
155
|
If any two records share the same field value, throws an exception.
|
|
144
156
|
|
|
145
|
-
:param wrapper_type: The record model wrapper to use.
|
|
157
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
146
158
|
:param field: The field to query and map on.
|
|
147
159
|
:param value_list: The values of the field to query on.
|
|
148
160
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
@@ -150,29 +162,32 @@ class RecordHandler:
|
|
|
150
162
|
:param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
|
|
151
163
|
:param mapping_field: If provided, use this field to map against instead of the field that was queried on.
|
|
152
164
|
:return: The record models for the queried records mapped by field values to the record with that value.
|
|
165
|
+
If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
|
|
166
|
+
instead of WrappedRecordModels.
|
|
153
167
|
"""
|
|
154
168
|
if mapping_field is None:
|
|
155
169
|
mapping_field = field
|
|
156
170
|
return self.map_by_unique_field(self.query_models(wrapper_type, field, value_list, page_limit, page_size),
|
|
157
171
|
mapping_field)
|
|
158
172
|
|
|
159
|
-
def query_models_with_criteria(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
|
|
173
|
+
def query_models_with_criteria(self, wrapper_type: type[WrappedType] | str, field: FieldIdentifier,
|
|
160
174
|
value_list: Iterable[FieldValue],
|
|
161
175
|
paging_criteria: DataRecordPojoPageCriteria | None = None,
|
|
162
176
|
page_limit: int | None = None) \
|
|
163
|
-
-> tuple[list[WrappedType], DataRecordPojoPageCriteria]:
|
|
177
|
+
-> tuple[list[WrappedType] | list[PyRecordModel], DataRecordPojoPageCriteria]:
|
|
164
178
|
"""
|
|
165
179
|
Shorthand for using the data record manager to query for a list of data records by field value
|
|
166
180
|
and then converting the results into a list of record models.
|
|
167
181
|
|
|
168
|
-
:param wrapper_type: The record model wrapper to use.
|
|
182
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
169
183
|
:param field: The field to query on.
|
|
170
184
|
:param value_list: The values of the field to query on.
|
|
171
185
|
:param paging_criteria: The paging criteria to start the query with.
|
|
172
186
|
:param page_limit: The maximum number of pages to query from the starting criteria. If None, exhausts all
|
|
173
187
|
possible pages. This parameter only functions if you set a page size in the paging criteria or the platform
|
|
174
188
|
enforces a page size.
|
|
175
|
-
:return: The record models for the queried records and the final paging criteria.
|
|
189
|
+
:return: The record models for the queried records and the final paging criteria. If a data type name was used
|
|
190
|
+
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
176
191
|
"""
|
|
177
192
|
dt: str = wrapper_type.get_wrapper_data_type_name()
|
|
178
193
|
field: str = AliasUtil.to_data_field_name(field)
|
|
@@ -180,104 +195,111 @@ class RecordHandler:
|
|
|
180
195
|
pager.max_page = page_limit
|
|
181
196
|
return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
|
|
182
197
|
|
|
183
|
-
def query_models_by_id(self, wrapper_type: type[WrappedType], ids: Iterable[int],
|
|
184
|
-
page_limit: int | None = None, page_size: int | None = None)
|
|
198
|
+
def query_models_by_id(self, wrapper_type: type[WrappedType] | str, ids: Iterable[int],
|
|
199
|
+
page_limit: int | None = None, page_size: int | None = None) \
|
|
200
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
185
201
|
"""
|
|
186
202
|
Shorthand for using the data record manager to query for a list of data records by record ID
|
|
187
203
|
and then converting the results into a list of record models.
|
|
188
204
|
|
|
189
|
-
:param wrapper_type: The record model wrapper to use.
|
|
205
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
190
206
|
:param ids: The list of record IDs to query.
|
|
191
207
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
192
208
|
only functions if you set a page size or the platform enforces a page size.
|
|
193
209
|
:param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
|
|
194
|
-
:return: The record models for the queried records.
|
|
210
|
+
:return: The record models for the queried records. If a data type name was used instead of a model wrapper,
|
|
211
|
+
then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
195
212
|
"""
|
|
196
213
|
criteria: DataRecordPojoPageCriteria | None = None
|
|
197
214
|
if page_size is not None:
|
|
198
215
|
criteria = DataRecordPojoPageCriteria(page_size=page_size)
|
|
199
216
|
return self.query_models_by_id_with_criteria(wrapper_type, ids, criteria, page_limit)[0]
|
|
200
217
|
|
|
201
|
-
def query_models_by_id_with_criteria(self, wrapper_type: type[WrappedType], ids: Iterable[int],
|
|
218
|
+
def query_models_by_id_with_criteria(self, wrapper_type: type[WrappedType] | str, ids: Iterable[int],
|
|
202
219
|
paging_criteria: DataRecordPojoPageCriteria | None = None,
|
|
203
220
|
page_limit: int | None = None) \
|
|
204
|
-
-> tuple[list[WrappedType], DataRecordPojoPageCriteria]:
|
|
221
|
+
-> tuple[list[WrappedType] | list[PyRecordModel], DataRecordPojoPageCriteria]:
|
|
205
222
|
"""
|
|
206
223
|
Shorthand for using the data record manager to query for a list of data records by record ID
|
|
207
224
|
and then converting the results into a list of record models.
|
|
208
225
|
|
|
209
|
-
:param wrapper_type: The record model wrapper to use.
|
|
226
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
210
227
|
:param ids: The list of record IDs to query.
|
|
211
228
|
:param paging_criteria: The paging criteria to start the query with.
|
|
212
229
|
:param page_limit: The maximum number of pages to query from the starting criteria. If None, exhausts all
|
|
213
230
|
possible pages. This parameter only functions if you set a page size in the paging criteria or the platform
|
|
214
231
|
enforces a page size.
|
|
215
|
-
:return: The record models for the queried records and the final paging criteria.
|
|
232
|
+
:return: The record models for the queried records and the final paging criteria. If a data type name was used
|
|
233
|
+
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
216
234
|
"""
|
|
217
|
-
dt: str =
|
|
235
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
218
236
|
pager = QueryDataRecordByIdListAutoPager(dt, list(ids), self.user, paging_criteria)
|
|
219
237
|
pager.max_page = page_limit
|
|
220
238
|
return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
|
|
221
239
|
|
|
222
|
-
def query_models_by_id_and_map(self, wrapper_type: type[WrappedType], ids: Iterable[int],
|
|
240
|
+
def query_models_by_id_and_map(self, wrapper_type: type[WrappedType] | str, ids: Iterable[int],
|
|
223
241
|
page_limit: int | None = None, page_size: int | None = None) \
|
|
224
|
-
-> dict[int, WrappedType]:
|
|
242
|
+
-> dict[int, WrappedType | PyRecordModel]:
|
|
225
243
|
"""
|
|
226
244
|
Shorthand for using the data record manager to query for a list of data records by record ID
|
|
227
245
|
and then converting the results into a dictionary of record ID to the record model for that ID.
|
|
228
246
|
|
|
229
|
-
:param wrapper_type: The record model wrapper to use.
|
|
247
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
230
248
|
:param ids: The list of record IDs to query.
|
|
231
249
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
232
250
|
only functions if you set a page size or the platform enforces a page size.
|
|
233
251
|
:param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
|
|
234
252
|
:return: The record models for the queried records mapped in a dictionary by their record ID.
|
|
253
|
+
If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
|
|
254
|
+
instead of WrappedRecordModels.
|
|
235
255
|
"""
|
|
236
256
|
return {AliasUtil.to_record_id(x): x for x in self.query_models_by_id(wrapper_type, ids, page_limit, page_size)}
|
|
237
257
|
|
|
238
|
-
def query_all_models(self, wrapper_type: type[WrappedType], page_limit: int | None = None,
|
|
239
|
-
page_size: int | None = None) -> list[WrappedType]:
|
|
258
|
+
def query_all_models(self, wrapper_type: type[WrappedType] | str, page_limit: int | None = None,
|
|
259
|
+
page_size: int | None = None) -> list[WrappedType] | list[PyRecordModel]:
|
|
240
260
|
"""
|
|
241
261
|
Shorthand for using the data record manager to query for all data records of a given type
|
|
242
262
|
and then converting the results into a list of record models.
|
|
243
263
|
|
|
244
|
-
:param wrapper_type: The record model wrapper to use.
|
|
264
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
245
265
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
246
266
|
only functions if you set a page size or the platform enforces a page size.
|
|
247
267
|
:param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
|
|
248
|
-
:return: The record models for the queried records.
|
|
268
|
+
:return: The record models for the queried records. If a data type name was used instead of a model wrapper,
|
|
269
|
+
then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
249
270
|
"""
|
|
250
271
|
criteria: DataRecordPojoPageCriteria | None = None
|
|
251
272
|
if page_size is not None:
|
|
252
273
|
criteria = DataRecordPojoPageCriteria(page_size=page_size)
|
|
253
274
|
return self.query_all_models_with_criteria(wrapper_type, criteria, page_limit)[0]
|
|
254
275
|
|
|
255
|
-
def query_all_models_with_criteria(self, wrapper_type: type[WrappedType],
|
|
276
|
+
def query_all_models_with_criteria(self, wrapper_type: type[WrappedType] | str,
|
|
256
277
|
paging_criteria: DataRecordPojoPageCriteria | None = None,
|
|
257
278
|
page_limit: int | None = None) \
|
|
258
|
-
-> tuple[list[WrappedType], DataRecordPojoPageCriteria]:
|
|
279
|
+
-> tuple[list[WrappedType] | list[PyRecordModel], DataRecordPojoPageCriteria]:
|
|
259
280
|
"""
|
|
260
281
|
Shorthand for using the data record manager to query for all data records of a given type
|
|
261
282
|
and then converting the results into a list of record models.
|
|
262
283
|
|
|
263
|
-
:param wrapper_type: The record model wrapper to use.
|
|
284
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
264
285
|
:param paging_criteria: The paging criteria to start the query with.
|
|
265
286
|
:param page_limit: The maximum number of pages to query from the starting criteria. If None, exhausts all
|
|
266
287
|
possible pages. This parameter only functions if you set a page size in the paging criteria or the platform
|
|
267
288
|
enforces a page size.
|
|
268
|
-
:return: The record models for the queried records and the final paging criteria.
|
|
289
|
+
:return: The record models for the queried records and the final paging criteria. If a data type name was used
|
|
290
|
+
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
269
291
|
"""
|
|
270
292
|
dt: str = wrapper_type.get_wrapper_data_type_name()
|
|
271
293
|
pager = QueryAllRecordsOfTypeAutoPager(dt, self.user, paging_criteria)
|
|
272
294
|
pager.max_page = page_limit
|
|
273
295
|
return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
|
|
274
296
|
|
|
275
|
-
def query_models_by_report(self, wrapper_type: type[WrappedType],
|
|
297
|
+
def query_models_by_report(self, wrapper_type: type[WrappedType] | str,
|
|
276
298
|
report_name: str | RawReportTerm | CustomReportCriteria,
|
|
277
299
|
filters: dict[FieldIdentifierKey, Iterable[FieldValue]] | None = None,
|
|
278
300
|
page_limit: int | None = None,
|
|
279
301
|
page_size: int | None = None,
|
|
280
|
-
page_number: int | None = None) -> list[WrappedType]:
|
|
302
|
+
page_number: int | None = None) -> list[WrappedType] | list[PyRecordModel]:
|
|
281
303
|
"""
|
|
282
304
|
Run a report and use the results of that report to query for and return the records in the report results.
|
|
283
305
|
First runs the report, then runs a data record manager query on the results of the custom report.
|
|
@@ -287,7 +309,7 @@ class RecordHandler:
|
|
|
287
309
|
|
|
288
310
|
Any given custom report criteria should only have columns from a single data type.
|
|
289
311
|
|
|
290
|
-
:param wrapper_type: The record model wrapper to use.
|
|
312
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
291
313
|
:param report_name: The name of a system report, or a raw report term for a quick report, or custom report
|
|
292
314
|
criteria for a custom report.
|
|
293
315
|
:param filters: If provided, filter the results of the report using the given mapping of headers to values to
|
|
@@ -299,7 +321,8 @@ class RecordHandler:
|
|
|
299
321
|
:param page_number: The page number to start the search from, If None, starts on the first page.
|
|
300
322
|
If the input report is a custom report criteria, uses the value from the criteria, unless this value is
|
|
301
323
|
not None, in which case it overwrites the given report's value. Note that the number of the first page is 0.
|
|
302
|
-
:return: The record models for the queried records that matched the given report.
|
|
324
|
+
:return: The record models for the queried records that matched the given report. If a data type name was used
|
|
325
|
+
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
303
326
|
"""
|
|
304
327
|
warnings.warn("Deprecated in favor of the [System/Custom/Quick]ReportRecordAutoPager classes.", DeprecationWarning)
|
|
305
328
|
if isinstance(report_name, str):
|
|
@@ -309,7 +332,7 @@ class RecordHandler:
|
|
|
309
332
|
results: list[dict[str, FieldValue]] = CustomReportUtil.run_quick_report(self.user, report_name, filters,
|
|
310
333
|
page_limit, page_size, page_number)
|
|
311
334
|
elif isinstance(report_name, CustomReportCriteria):
|
|
312
|
-
dt: str =
|
|
335
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
313
336
|
# Ensure that the root data type is the one we're looking for.
|
|
314
337
|
report_name.root_data_type = dt
|
|
315
338
|
# Raise an exception if any column in the report doesn't match the given data type.
|
|
@@ -329,35 +352,38 @@ class RecordHandler:
|
|
|
329
352
|
ids: list[int] = [row["RecordId"] for row in results]
|
|
330
353
|
return self.query_models_by_id(wrapper_type, ids)
|
|
331
354
|
|
|
332
|
-
def add_model(self, wrapper_type: type[WrappedType]) -> WrappedType:
|
|
355
|
+
def add_model(self, wrapper_type: type[WrappedType] | str) -> WrappedType | PyRecordModel:
|
|
333
356
|
"""
|
|
334
357
|
Shorthand for using the instance manager to add a new record model of the given type.
|
|
335
358
|
|
|
336
|
-
:param wrapper_type: The record model wrapper to use.
|
|
337
|
-
:return: The newly added record model.
|
|
359
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the record.
|
|
360
|
+
:return: The newly added record model. If a data type name was used instead of a model wrapper, then the
|
|
361
|
+
returned record will be a PyRecordModel instead of a WrappedRecordModel.
|
|
338
362
|
"""
|
|
339
363
|
return self.inst_man.add_new_record_of_type(wrapper_type)
|
|
340
364
|
|
|
341
|
-
def add_models(self, wrapper_type: type[WrappedType], num: int) -> list[WrappedType]:
|
|
365
|
+
def add_models(self, wrapper_type: type[WrappedType] | str, num: int) -> list[WrappedType] | list[PyRecordModel]:
|
|
342
366
|
"""
|
|
343
367
|
Shorthand for using the instance manager to add new record models of the given type.
|
|
344
368
|
|
|
345
|
-
:param wrapper_type: The record model wrapper to use.
|
|
369
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
346
370
|
:param num: The number of models to create.
|
|
347
|
-
:return: The newly added record models.
|
|
371
|
+
:return: The newly added record models. If a data type name was used instead of a model wrapper, then the
|
|
372
|
+
returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
348
373
|
"""
|
|
349
374
|
return self.inst_man.add_new_records_of_type(num, wrapper_type)
|
|
350
375
|
|
|
351
|
-
def add_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldIdentifierMap]) \
|
|
352
|
-
-> list[WrappedType]:
|
|
376
|
+
def add_models_with_data(self, wrapper_type: type[WrappedType] | str, fields: list[FieldIdentifierMap]) \
|
|
377
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
353
378
|
"""
|
|
354
379
|
Shorthand for using the instance manager to add new models of the given type, and then initializing all those
|
|
355
380
|
models with the given fields.
|
|
356
381
|
|
|
357
|
-
:param wrapper_type: The record model wrapper to use.
|
|
382
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
358
383
|
:param fields: A list of field maps to initialize the record models with.
|
|
359
384
|
:return: The newly added record models with the provided fields set. The records will be in the same order as
|
|
360
|
-
the fields in the fields list.
|
|
385
|
+
the fields in the fields list. If a data type name was used instead of a model wrapper, then the returned
|
|
386
|
+
records will be PyRecordModels instead of WrappedRecordModels.
|
|
361
387
|
"""
|
|
362
388
|
fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(fields)
|
|
363
389
|
models: list[WrappedType] = self.add_models(wrapper_type, len(fields))
|
|
@@ -365,8 +391,9 @@ class RecordHandler:
|
|
|
365
391
|
model.set_field_values(field_list)
|
|
366
392
|
return models
|
|
367
393
|
|
|
368
|
-
def find_or_add_model(self, wrapper_type: type[WrappedType], primary_identifier: FieldIdentifier,
|
|
369
|
-
id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None)
|
|
394
|
+
def find_or_add_model(self, wrapper_type: type[WrappedType] | str, primary_identifier: FieldIdentifier,
|
|
395
|
+
id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None) \
|
|
396
|
+
-> WrappedType | PyRecordModel:
|
|
370
397
|
"""
|
|
371
398
|
Find a unique record that matches the given field values. If no such records exist, add a record model to the
|
|
372
399
|
cache with the identifying fields set to the desired values. This record will be created in the system when
|
|
@@ -377,12 +404,14 @@ class RecordHandler:
|
|
|
377
404
|
|
|
378
405
|
Makes a webservice call to query for the existing record.
|
|
379
406
|
|
|
380
|
-
:param wrapper_type: The record model wrapper to use.
|
|
407
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the record.
|
|
381
408
|
:param primary_identifier: The data field name of the field to search on.
|
|
382
409
|
:param id_value: The value of the identifying field to search for.
|
|
383
410
|
:param secondary_identifiers: Optional fields used to filter the records that are returned after searching on
|
|
384
411
|
the primary identifier.
|
|
385
412
|
:return: The record model with the identifying field value, either pulled from the system or newly created.
|
|
413
|
+
If a data type name was used instead of a model wrapper, then the returned record will be a PyRecordModel
|
|
414
|
+
instead of a WrappedRecordModel.
|
|
386
415
|
"""
|
|
387
416
|
# PR-46335: Initialize the secondary identifiers parameter if None is provided to avoid an exception.
|
|
388
417
|
# If no secondary identifiers were provided, use an empty dictionary.
|
|
@@ -403,22 +432,23 @@ class RecordHandler:
|
|
|
403
432
|
secondary_identifiers.update({primary_identifier: id_value})
|
|
404
433
|
return self.add_models_with_data(wrapper_type, [secondary_identifiers])[0]
|
|
405
434
|
|
|
406
|
-
def create_models(self, wrapper_type: type[WrappedType], num: int) -> list[WrappedType]:
|
|
435
|
+
def create_models(self, wrapper_type: type[WrappedType] | str, num: int) -> list[WrappedType] | list[PyRecordModel]:
|
|
407
436
|
"""
|
|
408
437
|
Shorthand for creating new records via the data record manager and then returning them as wrapped
|
|
409
438
|
record models. Useful in cases where your record model needs to have a valid record ID.
|
|
410
439
|
|
|
411
440
|
Makes a webservice call to create the data records.
|
|
412
441
|
|
|
413
|
-
:param wrapper_type: The record model wrapper to use.
|
|
442
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
414
443
|
:param num: The number of new records to create.
|
|
415
|
-
:return: The newly created record models.
|
|
444
|
+
:return: The newly created record models. If a data type name was used instead of a model wrapper, then the
|
|
445
|
+
returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
416
446
|
"""
|
|
417
447
|
dt: str = wrapper_type.get_wrapper_data_type_name()
|
|
418
448
|
return self.wrap_models(self.dr_man.add_data_records(dt, num), wrapper_type)
|
|
419
449
|
|
|
420
|
-
def create_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldIdentifierMap]) \
|
|
421
|
-
-> list[WrappedType]:
|
|
450
|
+
def create_models_with_data(self, wrapper_type: type[WrappedType] | str, fields: list[FieldIdentifierMap]) \
|
|
451
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
422
452
|
"""
|
|
423
453
|
Shorthand for creating new records via the data record manager with field data to initialize the records with
|
|
424
454
|
and then returning them as wrapped record models. Useful in cases where your record model needs to have a valid
|
|
@@ -426,17 +456,18 @@ class RecordHandler:
|
|
|
426
456
|
|
|
427
457
|
Makes a webservice call to create the data records.
|
|
428
458
|
|
|
429
|
-
:param wrapper_type: The record model wrapper to use.
|
|
459
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
430
460
|
:param fields: The field map list to initialize the new data records with.
|
|
431
|
-
:return: The newly created record models.
|
|
461
|
+
:return: The newly created record models. If a data type name was used instead of a model wrapper, then the
|
|
462
|
+
returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
432
463
|
"""
|
|
433
464
|
dt: str = wrapper_type.get_wrapper_data_type_name()
|
|
434
465
|
fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(fields)
|
|
435
466
|
return self.wrap_models(self.dr_man.add_data_records_with_data(dt, fields), wrapper_type)
|
|
436
467
|
|
|
437
|
-
def find_or_create_model(self, wrapper_type: type[WrappedType], primary_identifier: FieldIdentifier,
|
|
468
|
+
def find_or_create_model(self, wrapper_type: type[WrappedType] | str, primary_identifier: FieldIdentifier,
|
|
438
469
|
id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None) \
|
|
439
|
-
-> WrappedType:
|
|
470
|
+
-> WrappedType | PyRecordModel:
|
|
440
471
|
"""
|
|
441
472
|
Find a unique record that matches the given field values. If no such records exist, create one with the
|
|
442
473
|
identifying fields set to the desired values. If more than one record with the identifying values exists,
|
|
@@ -448,12 +479,14 @@ class RecordHandler:
|
|
|
448
479
|
Makes a webservice call to query for the existing record. Makes an additional webservice call if the record
|
|
449
480
|
needs to be created.
|
|
450
481
|
|
|
451
|
-
:param wrapper_type: The record model wrapper to use.
|
|
482
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the record.
|
|
452
483
|
:param primary_identifier: The data field name of the field to search on.
|
|
453
484
|
:param id_value: The value of the identifying field to search for.
|
|
454
485
|
:param secondary_identifiers: Optional fields used to filter the records that are returned after searching on
|
|
455
486
|
the primary identifier.
|
|
456
487
|
:return: The record model with the identifying field value, either pulled from the system or newly created.
|
|
488
|
+
If a data type name was used instead of a model wrapper, then the returned record will be a PyRecordModel
|
|
489
|
+
instead of a WrappedRecordModel.
|
|
457
490
|
"""
|
|
458
491
|
# PR-46335: Initialize the secondary identifiers parameter if None is provided to avoid an exception.
|
|
459
492
|
# If no secondary identifiers were provided, use an empty dictionary.
|
|
@@ -475,7 +508,8 @@ class RecordHandler:
|
|
|
475
508
|
return self.create_models_with_data(wrapper_type, [secondary_identifiers])[0]
|
|
476
509
|
|
|
477
510
|
@staticmethod
|
|
478
|
-
def map_to_parent(models: Iterable[
|
|
511
|
+
def map_to_parent(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType])\
|
|
512
|
+
-> dict[WrappedRecordModel, WrappedType]:
|
|
479
513
|
"""
|
|
480
514
|
Map a list of record models to a single parent of a given type. The parents must already be loaded.
|
|
481
515
|
|
|
@@ -484,14 +518,14 @@ class RecordHandler:
|
|
|
484
518
|
:return: A dict[ModelType, ParentType]. If an input model doesn't have a parent of the given parent type, then
|
|
485
519
|
it will map to None.
|
|
486
520
|
"""
|
|
487
|
-
return_dict: dict[
|
|
521
|
+
return_dict: dict[WrappedRecordModel, WrappedType] = {}
|
|
488
522
|
for model in models:
|
|
489
523
|
return_dict[model] = model.get_parent_of_type(parent_type)
|
|
490
524
|
return return_dict
|
|
491
525
|
|
|
492
526
|
@staticmethod
|
|
493
|
-
def map_to_parents(models: Iterable[
|
|
494
|
-
-> dict[
|
|
527
|
+
def map_to_parents(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
|
|
528
|
+
-> dict[WrappedRecordModel, list[WrappedType]]:
|
|
495
529
|
"""
|
|
496
530
|
Map a list of record models to a list parents of a given type. The parents must already be loaded.
|
|
497
531
|
|
|
@@ -500,14 +534,14 @@ class RecordHandler:
|
|
|
500
534
|
:return: A dict[ModelType, list[ParentType]]. If an input model doesn't have a parent of the given parent type,
|
|
501
535
|
then it will map to an empty list.
|
|
502
536
|
"""
|
|
503
|
-
return_dict: dict[
|
|
537
|
+
return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
|
|
504
538
|
for model in models:
|
|
505
539
|
return_dict[model] = model.get_parents_of_type(parent_type)
|
|
506
540
|
return return_dict
|
|
507
541
|
|
|
508
542
|
@staticmethod
|
|
509
|
-
def map_by_parent(models: Iterable[
|
|
510
|
-
-> dict[WrappedType,
|
|
543
|
+
def map_by_parent(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
|
|
544
|
+
-> dict[WrappedType, WrappedRecordModel]:
|
|
511
545
|
"""
|
|
512
546
|
Take a list of record models and map them by their parent. Essentially an inversion of map_to_parent.
|
|
513
547
|
If two records share the same parent, an exception will be thrown. The parents must already be loaded.
|
|
@@ -517,8 +551,8 @@ class RecordHandler:
|
|
|
517
551
|
:return: A dict[ParentType, ModelType]. If an input model doesn't have a parent of the given parent type,
|
|
518
552
|
then it will not be in the resulting dictionary.
|
|
519
553
|
"""
|
|
520
|
-
to_parent: dict[
|
|
521
|
-
by_parent: dict[WrappedType,
|
|
554
|
+
to_parent: dict[WrappedRecordModel, WrappedType] = RecordHandler.map_to_parent(models, parent_type)
|
|
555
|
+
by_parent: dict[WrappedType, WrappedRecordModel] = {}
|
|
522
556
|
for record, parent in to_parent.items():
|
|
523
557
|
if parent is None:
|
|
524
558
|
continue
|
|
@@ -529,8 +563,8 @@ class RecordHandler:
|
|
|
529
563
|
return by_parent
|
|
530
564
|
|
|
531
565
|
@staticmethod
|
|
532
|
-
def map_by_parents(models: Iterable[
|
|
533
|
-
-> dict[WrappedType, list[
|
|
566
|
+
def map_by_parents(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
|
|
567
|
+
-> dict[WrappedType, list[WrappedRecordModel]]:
|
|
534
568
|
"""
|
|
535
569
|
Take a list of record models and map them by their parents. Essentially an inversion of map_to_parents. Input
|
|
536
570
|
models that share a parent will end up in the same list. The parents must already be loaded.
|
|
@@ -540,15 +574,16 @@ class RecordHandler:
|
|
|
540
574
|
:return: A dict[ParentType, list[ModelType]]. If an input model doesn't have a parent of the given parent type,
|
|
541
575
|
then it will not be in the resulting dictionary.
|
|
542
576
|
"""
|
|
543
|
-
to_parents: dict[
|
|
544
|
-
by_parents: dict[WrappedType, list[
|
|
577
|
+
to_parents: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler.map_to_parents(models, parent_type)
|
|
578
|
+
by_parents: dict[WrappedType, list[WrappedRecordModel]] = {}
|
|
545
579
|
for record, parents in to_parents.items():
|
|
546
580
|
for parent in parents:
|
|
547
581
|
by_parents.setdefault(parent, []).append(record)
|
|
548
582
|
return by_parents
|
|
549
583
|
|
|
550
584
|
@staticmethod
|
|
551
|
-
def map_to_child(models: Iterable[
|
|
585
|
+
def map_to_child(models: Iterable[WrappedRecordModel], child_type: type[WrappedType])\
|
|
586
|
+
-> dict[WrappedRecordModel, WrappedType]:
|
|
552
587
|
"""
|
|
553
588
|
Map a list of record models to a single child of a given type. The children must already be loaded.
|
|
554
589
|
|
|
@@ -557,14 +592,14 @@ class RecordHandler:
|
|
|
557
592
|
:return: A dict[ModelType, ChildType]. If an input model doesn't have a child of the given child type, then
|
|
558
593
|
it will map to None.
|
|
559
594
|
"""
|
|
560
|
-
return_dict: dict[
|
|
595
|
+
return_dict: dict[WrappedRecordModel, WrappedType] = {}
|
|
561
596
|
for model in models:
|
|
562
597
|
return_dict[model] = model.get_child_of_type(child_type)
|
|
563
598
|
return return_dict
|
|
564
599
|
|
|
565
600
|
@staticmethod
|
|
566
|
-
def map_to_children(models: Iterable[
|
|
567
|
-
-> dict[
|
|
601
|
+
def map_to_children(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
|
|
602
|
+
-> dict[WrappedRecordModel, list[WrappedType]]:
|
|
568
603
|
"""
|
|
569
604
|
Map a list of record models to a list children of a given type. The children must already be loaded.
|
|
570
605
|
|
|
@@ -573,14 +608,14 @@ class RecordHandler:
|
|
|
573
608
|
:return: A dict[ModelType, list[ChildType]]. If an input model doesn't have children of the given child type,
|
|
574
609
|
then it will map to an empty list.
|
|
575
610
|
"""
|
|
576
|
-
return_dict: dict[
|
|
611
|
+
return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
|
|
577
612
|
for model in models:
|
|
578
613
|
return_dict[model] = model.get_children_of_type(child_type)
|
|
579
614
|
return return_dict
|
|
580
615
|
|
|
581
616
|
@staticmethod
|
|
582
|
-
def map_by_child(models: Iterable[
|
|
583
|
-
-> dict[WrappedType,
|
|
617
|
+
def map_by_child(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
|
|
618
|
+
-> dict[WrappedType, WrappedRecordModel]:
|
|
584
619
|
"""
|
|
585
620
|
Take a list of record models and map them by their children. Essentially an inversion of map_to_child.
|
|
586
621
|
If two records share the same child, an exception will be thrown. The children must already be loaded.
|
|
@@ -590,8 +625,8 @@ class RecordHandler:
|
|
|
590
625
|
:return: A dict[ChildType, ModelType]. If an input model doesn't have a child of the given child type,
|
|
591
626
|
then it will not be in the resulting dictionary.
|
|
592
627
|
"""
|
|
593
|
-
to_child: dict[
|
|
594
|
-
by_child: dict[WrappedType,
|
|
628
|
+
to_child: dict[WrappedRecordModel, WrappedType] = RecordHandler.map_to_child(models, child_type)
|
|
629
|
+
by_child: dict[WrappedType, WrappedRecordModel] = {}
|
|
595
630
|
for record, child in to_child.items():
|
|
596
631
|
if child is None:
|
|
597
632
|
continue
|
|
@@ -602,8 +637,8 @@ class RecordHandler:
|
|
|
602
637
|
return by_child
|
|
603
638
|
|
|
604
639
|
@staticmethod
|
|
605
|
-
def map_by_children(models: Iterable[
|
|
606
|
-
-> dict[WrappedType, list[
|
|
640
|
+
def map_by_children(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
|
|
641
|
+
-> dict[WrappedType, list[WrappedRecordModel]]:
|
|
607
642
|
"""
|
|
608
643
|
Take a list of record models and map them by their children. Essentially an inversion of map_to_children. Input
|
|
609
644
|
models that share a child will end up in the same list. The children must already be loaded.
|
|
@@ -613,8 +648,8 @@ class RecordHandler:
|
|
|
613
648
|
:return: A dict[ChildType, list[ModelType]]. If an input model doesn't have children of the given child type,
|
|
614
649
|
then it will not be in the resulting dictionary.
|
|
615
650
|
"""
|
|
616
|
-
to_children: dict[
|
|
617
|
-
by_children: dict[WrappedType, list[
|
|
651
|
+
to_children: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler.map_to_children(models, child_type)
|
|
652
|
+
by_children: dict[WrappedType, list[WrappedRecordModel]] = {}
|
|
618
653
|
for record, children in to_children.items():
|
|
619
654
|
for child in children:
|
|
620
655
|
by_children.setdefault(child, []).append(record)
|
|
@@ -1072,18 +1107,19 @@ class RecordHandler:
|
|
|
1072
1107
|
ret_dict.update({model: self.inst_man.wrap(current[0], wrapper_type) if current else None})
|
|
1073
1108
|
return ret_dict
|
|
1074
1109
|
|
|
1075
|
-
def __find_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: FieldValue,
|
|
1076
|
-
secondary_identifiers: FieldIdentifierMap | None = None) -> WrappedType | None:
|
|
1110
|
+
def __find_model(self, wrapper_type: type[WrappedType] | str, primary_identifier: str, id_value: FieldValue,
|
|
1111
|
+
secondary_identifiers: FieldIdentifierMap | None = None) -> WrappedType | PyRecordModel | None:
|
|
1077
1112
|
"""
|
|
1078
1113
|
Find a record from the system that matches the given field values. The primary identifier and value is used
|
|
1079
1114
|
to query for the record, then the secondary identifiers may be optionally provided to further filter the
|
|
1080
1115
|
returned results. If no record is found with these filters, returns None.
|
|
1081
1116
|
"""
|
|
1082
1117
|
# Query for all records that match the primary identifier.
|
|
1083
|
-
results: list[WrappedType] = self.query_models(wrapper_type, primary_identifier,
|
|
1118
|
+
results: list[WrappedType] | list[PyRecordModel] = self.query_models(wrapper_type, primary_identifier,
|
|
1119
|
+
[id_value])
|
|
1084
1120
|
|
|
1085
1121
|
# Find the one record, if any, that matches the secondary identifiers.
|
|
1086
|
-
unique_record: WrappedType | None = None
|
|
1122
|
+
unique_record: WrappedType | PyRecordModel | None = None
|
|
1087
1123
|
for result in results:
|
|
1088
1124
|
matches_all: bool = True
|
|
1089
1125
|
for field, value in secondary_identifiers.items():
|
|
@@ -1093,7 +1129,7 @@ class RecordHandler:
|
|
|
1093
1129
|
if matches_all:
|
|
1094
1130
|
# If a previous record in the results already matched all identifiers, then throw an exception.
|
|
1095
1131
|
if unique_record is not None:
|
|
1096
|
-
raise SapioException(f"More than one record of type {
|
|
1132
|
+
raise SapioException(f"More than one record of type {AliasUtil.to_data_type_name(wrapper_type)} "
|
|
1097
1133
|
f"encountered in system that matches all provided identifiers.")
|
|
1098
1134
|
unique_record = result
|
|
1099
1135
|
return unique_record
|