sapiopycommons 2025.2.25a450__py3-none-any.whl → 2025.3.6a453__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 +61 -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 +151 -105
- {sapiopycommons-2025.2.25a450.dist-info → sapiopycommons-2025.3.6a453.dist-info}/METADATA +1 -1
- {sapiopycommons-2025.2.25a450.dist-info → sapiopycommons-2025.3.6a453.dist-info}/RECORD +13 -12
- {sapiopycommons-2025.2.25a450.dist-info → sapiopycommons-2025.3.6a453.dist-info}/WHEEL +0 -0
- {sapiopycommons-2025.2.25a450.dist-info → sapiopycommons-2025.3.6a453.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,134 +162,150 @@ 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
|
-
dt: str =
|
|
192
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
193
|
+
if isinstance(wrapper_type, str):
|
|
194
|
+
wrapper_type = None
|
|
178
195
|
field: str = AliasUtil.to_data_field_name(field)
|
|
179
196
|
pager = QueryDataRecordsAutoPager(dt, field, list(value_list), self.user, paging_criteria)
|
|
180
197
|
pager.max_page = page_limit
|
|
181
198
|
return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
|
|
182
199
|
|
|
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)
|
|
200
|
+
def query_models_by_id(self, wrapper_type: type[WrappedType] | str, ids: Iterable[int],
|
|
201
|
+
page_limit: int | None = None, page_size: int | None = None) \
|
|
202
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
185
203
|
"""
|
|
186
204
|
Shorthand for using the data record manager to query for a list of data records by record ID
|
|
187
205
|
and then converting the results into a list of record models.
|
|
188
206
|
|
|
189
|
-
:param wrapper_type: The record model wrapper to use.
|
|
207
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
190
208
|
:param ids: The list of record IDs to query.
|
|
191
209
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
192
210
|
only functions if you set a page size or the platform enforces a page size.
|
|
193
211
|
: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.
|
|
212
|
+
:return: The record models for the queried records. If a data type name was used instead of a model wrapper,
|
|
213
|
+
then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
195
214
|
"""
|
|
196
215
|
criteria: DataRecordPojoPageCriteria | None = None
|
|
197
216
|
if page_size is not None:
|
|
198
217
|
criteria = DataRecordPojoPageCriteria(page_size=page_size)
|
|
199
218
|
return self.query_models_by_id_with_criteria(wrapper_type, ids, criteria, page_limit)[0]
|
|
200
219
|
|
|
201
|
-
def query_models_by_id_with_criteria(self, wrapper_type: type[WrappedType], ids: Iterable[int],
|
|
220
|
+
def query_models_by_id_with_criteria(self, wrapper_type: type[WrappedType] | str, ids: Iterable[int],
|
|
202
221
|
paging_criteria: DataRecordPojoPageCriteria | None = None,
|
|
203
222
|
page_limit: int | None = None) \
|
|
204
|
-
-> tuple[list[WrappedType], DataRecordPojoPageCriteria]:
|
|
223
|
+
-> tuple[list[WrappedType] | list[PyRecordModel], DataRecordPojoPageCriteria]:
|
|
205
224
|
"""
|
|
206
225
|
Shorthand for using the data record manager to query for a list of data records by record ID
|
|
207
226
|
and then converting the results into a list of record models.
|
|
208
227
|
|
|
209
|
-
:param wrapper_type: The record model wrapper to use.
|
|
228
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
210
229
|
:param ids: The list of record IDs to query.
|
|
211
230
|
:param paging_criteria: The paging criteria to start the query with.
|
|
212
231
|
:param page_limit: The maximum number of pages to query from the starting criteria. If None, exhausts all
|
|
213
232
|
possible pages. This parameter only functions if you set a page size in the paging criteria or the platform
|
|
214
233
|
enforces a page size.
|
|
215
|
-
:return: The record models for the queried records and the final paging criteria.
|
|
234
|
+
:return: The record models for the queried records and the final paging criteria. If a data type name was used
|
|
235
|
+
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
216
236
|
"""
|
|
217
|
-
dt: str =
|
|
237
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
238
|
+
if isinstance(wrapper_type, str):
|
|
239
|
+
wrapper_type = None
|
|
218
240
|
pager = QueryDataRecordByIdListAutoPager(dt, list(ids), self.user, paging_criteria)
|
|
219
241
|
pager.max_page = page_limit
|
|
220
242
|
return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
|
|
221
243
|
|
|
222
|
-
def query_models_by_id_and_map(self, wrapper_type: type[WrappedType], ids: Iterable[int],
|
|
244
|
+
def query_models_by_id_and_map(self, wrapper_type: type[WrappedType] | str, ids: Iterable[int],
|
|
223
245
|
page_limit: int | None = None, page_size: int | None = None) \
|
|
224
|
-
-> dict[int, WrappedType]:
|
|
246
|
+
-> dict[int, WrappedType | PyRecordModel]:
|
|
225
247
|
"""
|
|
226
248
|
Shorthand for using the data record manager to query for a list of data records by record ID
|
|
227
249
|
and then converting the results into a dictionary of record ID to the record model for that ID.
|
|
228
250
|
|
|
229
|
-
:param wrapper_type: The record model wrapper to use.
|
|
251
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
230
252
|
:param ids: The list of record IDs to query.
|
|
231
253
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
232
254
|
only functions if you set a page size or the platform enforces a page size.
|
|
233
255
|
:param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
|
|
234
256
|
:return: The record models for the queried records mapped in a dictionary by their record ID.
|
|
257
|
+
If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
|
|
258
|
+
instead of WrappedRecordModels.
|
|
235
259
|
"""
|
|
236
260
|
return {AliasUtil.to_record_id(x): x for x in self.query_models_by_id(wrapper_type, ids, page_limit, page_size)}
|
|
237
261
|
|
|
238
|
-
def query_all_models(self, wrapper_type: type[WrappedType], page_limit: int | None = None,
|
|
239
|
-
page_size: int | None = None) -> list[WrappedType]:
|
|
262
|
+
def query_all_models(self, wrapper_type: type[WrappedType] | str, page_limit: int | None = None,
|
|
263
|
+
page_size: int | None = None) -> list[WrappedType] | list[PyRecordModel]:
|
|
240
264
|
"""
|
|
241
265
|
Shorthand for using the data record manager to query for all data records of a given type
|
|
242
266
|
and then converting the results into a list of record models.
|
|
243
267
|
|
|
244
|
-
:param wrapper_type: The record model wrapper to use.
|
|
268
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
245
269
|
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
|
|
246
270
|
only functions if you set a page size or the platform enforces a page size.
|
|
247
271
|
: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.
|
|
272
|
+
:return: The record models for the queried records. If a data type name was used instead of a model wrapper,
|
|
273
|
+
then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
249
274
|
"""
|
|
250
275
|
criteria: DataRecordPojoPageCriteria | None = None
|
|
251
276
|
if page_size is not None:
|
|
252
277
|
criteria = DataRecordPojoPageCriteria(page_size=page_size)
|
|
253
278
|
return self.query_all_models_with_criteria(wrapper_type, criteria, page_limit)[0]
|
|
254
279
|
|
|
255
|
-
def query_all_models_with_criteria(self, wrapper_type: type[WrappedType],
|
|
280
|
+
def query_all_models_with_criteria(self, wrapper_type: type[WrappedType] | str,
|
|
256
281
|
paging_criteria: DataRecordPojoPageCriteria | None = None,
|
|
257
282
|
page_limit: int | None = None) \
|
|
258
|
-
-> tuple[list[WrappedType], DataRecordPojoPageCriteria]:
|
|
283
|
+
-> tuple[list[WrappedType] | list[PyRecordModel], DataRecordPojoPageCriteria]:
|
|
259
284
|
"""
|
|
260
285
|
Shorthand for using the data record manager to query for all data records of a given type
|
|
261
286
|
and then converting the results into a list of record models.
|
|
262
287
|
|
|
263
|
-
:param wrapper_type: The record model wrapper to use.
|
|
288
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
264
289
|
:param paging_criteria: The paging criteria to start the query with.
|
|
265
290
|
:param page_limit: The maximum number of pages to query from the starting criteria. If None, exhausts all
|
|
266
291
|
possible pages. This parameter only functions if you set a page size in the paging criteria or the platform
|
|
267
292
|
enforces a page size.
|
|
268
|
-
:return: The record models for the queried records and the final paging criteria.
|
|
293
|
+
:return: The record models for the queried records and the final paging criteria. If a data type name was used
|
|
294
|
+
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
269
295
|
"""
|
|
270
|
-
dt: str =
|
|
296
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
297
|
+
if isinstance(wrapper_type, str):
|
|
298
|
+
wrapper_type = None
|
|
271
299
|
pager = QueryAllRecordsOfTypeAutoPager(dt, self.user, paging_criteria)
|
|
272
300
|
pager.max_page = page_limit
|
|
273
301
|
return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
|
|
274
302
|
|
|
275
|
-
def query_models_by_report(self, wrapper_type: type[WrappedType],
|
|
303
|
+
def query_models_by_report(self, wrapper_type: type[WrappedType] | str,
|
|
276
304
|
report_name: str | RawReportTerm | CustomReportCriteria,
|
|
277
305
|
filters: dict[FieldIdentifierKey, Iterable[FieldValue]] | None = None,
|
|
278
306
|
page_limit: int | None = None,
|
|
279
307
|
page_size: int | None = None,
|
|
280
|
-
page_number: int | None = None) -> list[WrappedType]:
|
|
308
|
+
page_number: int | None = None) -> list[WrappedType] | list[PyRecordModel]:
|
|
281
309
|
"""
|
|
282
310
|
Run a report and use the results of that report to query for and return the records in the report results.
|
|
283
311
|
First runs the report, then runs a data record manager query on the results of the custom report.
|
|
@@ -287,7 +315,7 @@ class RecordHandler:
|
|
|
287
315
|
|
|
288
316
|
Any given custom report criteria should only have columns from a single data type.
|
|
289
317
|
|
|
290
|
-
:param wrapper_type: The record model wrapper to use.
|
|
318
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
291
319
|
:param report_name: The name of a system report, or a raw report term for a quick report, or custom report
|
|
292
320
|
criteria for a custom report.
|
|
293
321
|
:param filters: If provided, filter the results of the report using the given mapping of headers to values to
|
|
@@ -299,7 +327,8 @@ class RecordHandler:
|
|
|
299
327
|
:param page_number: The page number to start the search from, If None, starts on the first page.
|
|
300
328
|
If the input report is a custom report criteria, uses the value from the criteria, unless this value is
|
|
301
329
|
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.
|
|
330
|
+
:return: The record models for the queried records that matched the given report. If a data type name was used
|
|
331
|
+
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
303
332
|
"""
|
|
304
333
|
warnings.warn("Deprecated in favor of the [System/Custom/Quick]ReportRecordAutoPager classes.", DeprecationWarning)
|
|
305
334
|
if isinstance(report_name, str):
|
|
@@ -309,7 +338,7 @@ class RecordHandler:
|
|
|
309
338
|
results: list[dict[str, FieldValue]] = CustomReportUtil.run_quick_report(self.user, report_name, filters,
|
|
310
339
|
page_limit, page_size, page_number)
|
|
311
340
|
elif isinstance(report_name, CustomReportCriteria):
|
|
312
|
-
dt: str =
|
|
341
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
313
342
|
# Ensure that the root data type is the one we're looking for.
|
|
314
343
|
report_name.root_data_type = dt
|
|
315
344
|
# Raise an exception if any column in the report doesn't match the given data type.
|
|
@@ -329,35 +358,38 @@ class RecordHandler:
|
|
|
329
358
|
ids: list[int] = [row["RecordId"] for row in results]
|
|
330
359
|
return self.query_models_by_id(wrapper_type, ids)
|
|
331
360
|
|
|
332
|
-
def add_model(self, wrapper_type: type[WrappedType]) -> WrappedType:
|
|
361
|
+
def add_model(self, wrapper_type: type[WrappedType] | str) -> WrappedType | PyRecordModel:
|
|
333
362
|
"""
|
|
334
363
|
Shorthand for using the instance manager to add a new record model of the given type.
|
|
335
364
|
|
|
336
|
-
:param wrapper_type: The record model wrapper to use.
|
|
337
|
-
:return: The newly added record model.
|
|
365
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the record.
|
|
366
|
+
:return: The newly added record model. If a data type name was used instead of a model wrapper, then the
|
|
367
|
+
returned record will be a PyRecordModel instead of a WrappedRecordModel.
|
|
338
368
|
"""
|
|
339
369
|
return self.inst_man.add_new_record_of_type(wrapper_type)
|
|
340
370
|
|
|
341
|
-
def add_models(self, wrapper_type: type[WrappedType], num: int) -> list[WrappedType]:
|
|
371
|
+
def add_models(self, wrapper_type: type[WrappedType] | str, num: int) -> list[WrappedType] | list[PyRecordModel]:
|
|
342
372
|
"""
|
|
343
373
|
Shorthand for using the instance manager to add new record models of the given type.
|
|
344
374
|
|
|
345
|
-
:param wrapper_type: The record model wrapper to use.
|
|
375
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
346
376
|
:param num: The number of models to create.
|
|
347
|
-
:return: The newly added record models.
|
|
377
|
+
:return: The newly added record models. If a data type name was used instead of a model wrapper, then the
|
|
378
|
+
returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
348
379
|
"""
|
|
349
380
|
return self.inst_man.add_new_records_of_type(num, wrapper_type)
|
|
350
381
|
|
|
351
|
-
def add_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldIdentifierMap]) \
|
|
352
|
-
-> list[WrappedType]:
|
|
382
|
+
def add_models_with_data(self, wrapper_type: type[WrappedType] | str, fields: list[FieldIdentifierMap]) \
|
|
383
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
353
384
|
"""
|
|
354
385
|
Shorthand for using the instance manager to add new models of the given type, and then initializing all those
|
|
355
386
|
models with the given fields.
|
|
356
387
|
|
|
357
|
-
:param wrapper_type: The record model wrapper to use.
|
|
388
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
358
389
|
:param fields: A list of field maps to initialize the record models with.
|
|
359
390
|
: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.
|
|
391
|
+
the fields in the fields list. If a data type name was used instead of a model wrapper, then the returned
|
|
392
|
+
records will be PyRecordModels instead of WrappedRecordModels.
|
|
361
393
|
"""
|
|
362
394
|
fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(fields)
|
|
363
395
|
models: list[WrappedType] = self.add_models(wrapper_type, len(fields))
|
|
@@ -365,8 +397,9 @@ class RecordHandler:
|
|
|
365
397
|
model.set_field_values(field_list)
|
|
366
398
|
return models
|
|
367
399
|
|
|
368
|
-
def find_or_add_model(self, wrapper_type: type[WrappedType], primary_identifier: FieldIdentifier,
|
|
369
|
-
id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None)
|
|
400
|
+
def find_or_add_model(self, wrapper_type: type[WrappedType] | str, primary_identifier: FieldIdentifier,
|
|
401
|
+
id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None) \
|
|
402
|
+
-> WrappedType | PyRecordModel:
|
|
370
403
|
"""
|
|
371
404
|
Find a unique record that matches the given field values. If no such records exist, add a record model to the
|
|
372
405
|
cache with the identifying fields set to the desired values. This record will be created in the system when
|
|
@@ -377,12 +410,14 @@ class RecordHandler:
|
|
|
377
410
|
|
|
378
411
|
Makes a webservice call to query for the existing record.
|
|
379
412
|
|
|
380
|
-
:param wrapper_type: The record model wrapper to use.
|
|
413
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the record.
|
|
381
414
|
:param primary_identifier: The data field name of the field to search on.
|
|
382
415
|
:param id_value: The value of the identifying field to search for.
|
|
383
416
|
:param secondary_identifiers: Optional fields used to filter the records that are returned after searching on
|
|
384
417
|
the primary identifier.
|
|
385
418
|
:return: The record model with the identifying field value, either pulled from the system or newly created.
|
|
419
|
+
If a data type name was used instead of a model wrapper, then the returned record will be a PyRecordModel
|
|
420
|
+
instead of a WrappedRecordModel.
|
|
386
421
|
"""
|
|
387
422
|
# PR-46335: Initialize the secondary identifiers parameter if None is provided to avoid an exception.
|
|
388
423
|
# If no secondary identifiers were provided, use an empty dictionary.
|
|
@@ -403,22 +438,25 @@ class RecordHandler:
|
|
|
403
438
|
secondary_identifiers.update({primary_identifier: id_value})
|
|
404
439
|
return self.add_models_with_data(wrapper_type, [secondary_identifiers])[0]
|
|
405
440
|
|
|
406
|
-
def create_models(self, wrapper_type: type[WrappedType], num: int) -> list[WrappedType]:
|
|
441
|
+
def create_models(self, wrapper_type: type[WrappedType] | str, num: int) -> list[WrappedType] | list[PyRecordModel]:
|
|
407
442
|
"""
|
|
408
443
|
Shorthand for creating new records via the data record manager and then returning them as wrapped
|
|
409
444
|
record models. Useful in cases where your record model needs to have a valid record ID.
|
|
410
445
|
|
|
411
446
|
Makes a webservice call to create the data records.
|
|
412
447
|
|
|
413
|
-
:param wrapper_type: The record model wrapper to use.
|
|
448
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
414
449
|
:param num: The number of new records to create.
|
|
415
|
-
:return: The newly created record models.
|
|
450
|
+
:return: The newly created record models. If a data type name was used instead of a model wrapper, then the
|
|
451
|
+
returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
416
452
|
"""
|
|
417
|
-
dt: str =
|
|
453
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
454
|
+
if isinstance(wrapper_type, str):
|
|
455
|
+
wrapper_type = None
|
|
418
456
|
return self.wrap_models(self.dr_man.add_data_records(dt, num), wrapper_type)
|
|
419
457
|
|
|
420
|
-
def create_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldIdentifierMap]) \
|
|
421
|
-
-> list[WrappedType]:
|
|
458
|
+
def create_models_with_data(self, wrapper_type: type[WrappedType] | str, fields: list[FieldIdentifierMap]) \
|
|
459
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
422
460
|
"""
|
|
423
461
|
Shorthand for creating new records via the data record manager with field data to initialize the records with
|
|
424
462
|
and then returning them as wrapped record models. Useful in cases where your record model needs to have a valid
|
|
@@ -426,17 +464,20 @@ class RecordHandler:
|
|
|
426
464
|
|
|
427
465
|
Makes a webservice call to create the data records.
|
|
428
466
|
|
|
429
|
-
:param wrapper_type: The record model wrapper to use.
|
|
467
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the records.
|
|
430
468
|
:param fields: The field map list to initialize the new data records with.
|
|
431
|
-
:return: The newly created record models.
|
|
469
|
+
:return: The newly created record models. If a data type name was used instead of a model wrapper, then the
|
|
470
|
+
returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
432
471
|
"""
|
|
433
|
-
dt: str =
|
|
472
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
473
|
+
if isinstance(wrapper_type, str):
|
|
474
|
+
wrapper_type = None
|
|
434
475
|
fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(fields)
|
|
435
476
|
return self.wrap_models(self.dr_man.add_data_records_with_data(dt, fields), wrapper_type)
|
|
436
477
|
|
|
437
|
-
def find_or_create_model(self, wrapper_type: type[WrappedType], primary_identifier: FieldIdentifier,
|
|
478
|
+
def find_or_create_model(self, wrapper_type: type[WrappedType] | str, primary_identifier: FieldIdentifier,
|
|
438
479
|
id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None) \
|
|
439
|
-
-> WrappedType:
|
|
480
|
+
-> WrappedType | PyRecordModel:
|
|
440
481
|
"""
|
|
441
482
|
Find a unique record that matches the given field values. If no such records exist, create one with the
|
|
442
483
|
identifying fields set to the desired values. If more than one record with the identifying values exists,
|
|
@@ -448,12 +489,14 @@ class RecordHandler:
|
|
|
448
489
|
Makes a webservice call to query for the existing record. Makes an additional webservice call if the record
|
|
449
490
|
needs to be created.
|
|
450
491
|
|
|
451
|
-
:param wrapper_type: The record model wrapper to use.
|
|
492
|
+
:param wrapper_type: The record model wrapper to use, or the data type name of the record.
|
|
452
493
|
:param primary_identifier: The data field name of the field to search on.
|
|
453
494
|
:param id_value: The value of the identifying field to search for.
|
|
454
495
|
:param secondary_identifiers: Optional fields used to filter the records that are returned after searching on
|
|
455
496
|
the primary identifier.
|
|
456
497
|
:return: The record model with the identifying field value, either pulled from the system or newly created.
|
|
498
|
+
If a data type name was used instead of a model wrapper, then the returned record will be a PyRecordModel
|
|
499
|
+
instead of a WrappedRecordModel.
|
|
457
500
|
"""
|
|
458
501
|
# PR-46335: Initialize the secondary identifiers parameter if None is provided to avoid an exception.
|
|
459
502
|
# If no secondary identifiers were provided, use an empty dictionary.
|
|
@@ -475,7 +518,8 @@ class RecordHandler:
|
|
|
475
518
|
return self.create_models_with_data(wrapper_type, [secondary_identifiers])[0]
|
|
476
519
|
|
|
477
520
|
@staticmethod
|
|
478
|
-
def map_to_parent(models: Iterable[
|
|
521
|
+
def map_to_parent(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType])\
|
|
522
|
+
-> dict[WrappedRecordModel, WrappedType]:
|
|
479
523
|
"""
|
|
480
524
|
Map a list of record models to a single parent of a given type. The parents must already be loaded.
|
|
481
525
|
|
|
@@ -484,14 +528,14 @@ class RecordHandler:
|
|
|
484
528
|
:return: A dict[ModelType, ParentType]. If an input model doesn't have a parent of the given parent type, then
|
|
485
529
|
it will map to None.
|
|
486
530
|
"""
|
|
487
|
-
return_dict: dict[
|
|
531
|
+
return_dict: dict[WrappedRecordModel, WrappedType] = {}
|
|
488
532
|
for model in models:
|
|
489
533
|
return_dict[model] = model.get_parent_of_type(parent_type)
|
|
490
534
|
return return_dict
|
|
491
535
|
|
|
492
536
|
@staticmethod
|
|
493
|
-
def map_to_parents(models: Iterable[
|
|
494
|
-
-> dict[
|
|
537
|
+
def map_to_parents(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
|
|
538
|
+
-> dict[WrappedRecordModel, list[WrappedType]]:
|
|
495
539
|
"""
|
|
496
540
|
Map a list of record models to a list parents of a given type. The parents must already be loaded.
|
|
497
541
|
|
|
@@ -500,14 +544,14 @@ class RecordHandler:
|
|
|
500
544
|
:return: A dict[ModelType, list[ParentType]]. If an input model doesn't have a parent of the given parent type,
|
|
501
545
|
then it will map to an empty list.
|
|
502
546
|
"""
|
|
503
|
-
return_dict: dict[
|
|
547
|
+
return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
|
|
504
548
|
for model in models:
|
|
505
549
|
return_dict[model] = model.get_parents_of_type(parent_type)
|
|
506
550
|
return return_dict
|
|
507
551
|
|
|
508
552
|
@staticmethod
|
|
509
|
-
def map_by_parent(models: Iterable[
|
|
510
|
-
-> dict[WrappedType,
|
|
553
|
+
def map_by_parent(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
|
|
554
|
+
-> dict[WrappedType, WrappedRecordModel]:
|
|
511
555
|
"""
|
|
512
556
|
Take a list of record models and map them by their parent. Essentially an inversion of map_to_parent.
|
|
513
557
|
If two records share the same parent, an exception will be thrown. The parents must already be loaded.
|
|
@@ -517,8 +561,8 @@ class RecordHandler:
|
|
|
517
561
|
:return: A dict[ParentType, ModelType]. If an input model doesn't have a parent of the given parent type,
|
|
518
562
|
then it will not be in the resulting dictionary.
|
|
519
563
|
"""
|
|
520
|
-
to_parent: dict[
|
|
521
|
-
by_parent: dict[WrappedType,
|
|
564
|
+
to_parent: dict[WrappedRecordModel, WrappedType] = RecordHandler.map_to_parent(models, parent_type)
|
|
565
|
+
by_parent: dict[WrappedType, WrappedRecordModel] = {}
|
|
522
566
|
for record, parent in to_parent.items():
|
|
523
567
|
if parent is None:
|
|
524
568
|
continue
|
|
@@ -529,8 +573,8 @@ class RecordHandler:
|
|
|
529
573
|
return by_parent
|
|
530
574
|
|
|
531
575
|
@staticmethod
|
|
532
|
-
def map_by_parents(models: Iterable[
|
|
533
|
-
-> dict[WrappedType, list[
|
|
576
|
+
def map_by_parents(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
|
|
577
|
+
-> dict[WrappedType, list[WrappedRecordModel]]:
|
|
534
578
|
"""
|
|
535
579
|
Take a list of record models and map them by their parents. Essentially an inversion of map_to_parents. Input
|
|
536
580
|
models that share a parent will end up in the same list. The parents must already be loaded.
|
|
@@ -540,15 +584,16 @@ class RecordHandler:
|
|
|
540
584
|
:return: A dict[ParentType, list[ModelType]]. If an input model doesn't have a parent of the given parent type,
|
|
541
585
|
then it will not be in the resulting dictionary.
|
|
542
586
|
"""
|
|
543
|
-
to_parents: dict[
|
|
544
|
-
by_parents: dict[WrappedType, list[
|
|
587
|
+
to_parents: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler.map_to_parents(models, parent_type)
|
|
588
|
+
by_parents: dict[WrappedType, list[WrappedRecordModel]] = {}
|
|
545
589
|
for record, parents in to_parents.items():
|
|
546
590
|
for parent in parents:
|
|
547
591
|
by_parents.setdefault(parent, []).append(record)
|
|
548
592
|
return by_parents
|
|
549
593
|
|
|
550
594
|
@staticmethod
|
|
551
|
-
def map_to_child(models: Iterable[
|
|
595
|
+
def map_to_child(models: Iterable[WrappedRecordModel], child_type: type[WrappedType])\
|
|
596
|
+
-> dict[WrappedRecordModel, WrappedType]:
|
|
552
597
|
"""
|
|
553
598
|
Map a list of record models to a single child of a given type. The children must already be loaded.
|
|
554
599
|
|
|
@@ -557,14 +602,14 @@ class RecordHandler:
|
|
|
557
602
|
:return: A dict[ModelType, ChildType]. If an input model doesn't have a child of the given child type, then
|
|
558
603
|
it will map to None.
|
|
559
604
|
"""
|
|
560
|
-
return_dict: dict[
|
|
605
|
+
return_dict: dict[WrappedRecordModel, WrappedType] = {}
|
|
561
606
|
for model in models:
|
|
562
607
|
return_dict[model] = model.get_child_of_type(child_type)
|
|
563
608
|
return return_dict
|
|
564
609
|
|
|
565
610
|
@staticmethod
|
|
566
|
-
def map_to_children(models: Iterable[
|
|
567
|
-
-> dict[
|
|
611
|
+
def map_to_children(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
|
|
612
|
+
-> dict[WrappedRecordModel, list[WrappedType]]:
|
|
568
613
|
"""
|
|
569
614
|
Map a list of record models to a list children of a given type. The children must already be loaded.
|
|
570
615
|
|
|
@@ -573,14 +618,14 @@ class RecordHandler:
|
|
|
573
618
|
:return: A dict[ModelType, list[ChildType]]. If an input model doesn't have children of the given child type,
|
|
574
619
|
then it will map to an empty list.
|
|
575
620
|
"""
|
|
576
|
-
return_dict: dict[
|
|
621
|
+
return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
|
|
577
622
|
for model in models:
|
|
578
623
|
return_dict[model] = model.get_children_of_type(child_type)
|
|
579
624
|
return return_dict
|
|
580
625
|
|
|
581
626
|
@staticmethod
|
|
582
|
-
def map_by_child(models: Iterable[
|
|
583
|
-
-> dict[WrappedType,
|
|
627
|
+
def map_by_child(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
|
|
628
|
+
-> dict[WrappedType, WrappedRecordModel]:
|
|
584
629
|
"""
|
|
585
630
|
Take a list of record models and map them by their children. Essentially an inversion of map_to_child.
|
|
586
631
|
If two records share the same child, an exception will be thrown. The children must already be loaded.
|
|
@@ -590,8 +635,8 @@ class RecordHandler:
|
|
|
590
635
|
:return: A dict[ChildType, ModelType]. If an input model doesn't have a child of the given child type,
|
|
591
636
|
then it will not be in the resulting dictionary.
|
|
592
637
|
"""
|
|
593
|
-
to_child: dict[
|
|
594
|
-
by_child: dict[WrappedType,
|
|
638
|
+
to_child: dict[WrappedRecordModel, WrappedType] = RecordHandler.map_to_child(models, child_type)
|
|
639
|
+
by_child: dict[WrappedType, WrappedRecordModel] = {}
|
|
595
640
|
for record, child in to_child.items():
|
|
596
641
|
if child is None:
|
|
597
642
|
continue
|
|
@@ -602,8 +647,8 @@ class RecordHandler:
|
|
|
602
647
|
return by_child
|
|
603
648
|
|
|
604
649
|
@staticmethod
|
|
605
|
-
def map_by_children(models: Iterable[
|
|
606
|
-
-> dict[WrappedType, list[
|
|
650
|
+
def map_by_children(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
|
|
651
|
+
-> dict[WrappedType, list[WrappedRecordModel]]:
|
|
607
652
|
"""
|
|
608
653
|
Take a list of record models and map them by their children. Essentially an inversion of map_to_children. Input
|
|
609
654
|
models that share a child will end up in the same list. The children must already be loaded.
|
|
@@ -613,8 +658,8 @@ class RecordHandler:
|
|
|
613
658
|
:return: A dict[ChildType, list[ModelType]]. If an input model doesn't have children of the given child type,
|
|
614
659
|
then it will not be in the resulting dictionary.
|
|
615
660
|
"""
|
|
616
|
-
to_children: dict[
|
|
617
|
-
by_children: dict[WrappedType, list[
|
|
661
|
+
to_children: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler.map_to_children(models, child_type)
|
|
662
|
+
by_children: dict[WrappedType, list[WrappedRecordModel]] = {}
|
|
618
663
|
for record, children in to_children.items():
|
|
619
664
|
for child in children:
|
|
620
665
|
by_children.setdefault(child, []).append(record)
|
|
@@ -1072,18 +1117,19 @@ class RecordHandler:
|
|
|
1072
1117
|
ret_dict.update({model: self.inst_man.wrap(current[0], wrapper_type) if current else None})
|
|
1073
1118
|
return ret_dict
|
|
1074
1119
|
|
|
1075
|
-
def __find_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: FieldValue,
|
|
1076
|
-
secondary_identifiers: FieldIdentifierMap | None = None) -> WrappedType | None:
|
|
1120
|
+
def __find_model(self, wrapper_type: type[WrappedType] | str, primary_identifier: str, id_value: FieldValue,
|
|
1121
|
+
secondary_identifiers: FieldIdentifierMap | None = None) -> WrappedType | PyRecordModel | None:
|
|
1077
1122
|
"""
|
|
1078
1123
|
Find a record from the system that matches the given field values. The primary identifier and value is used
|
|
1079
1124
|
to query for the record, then the secondary identifiers may be optionally provided to further filter the
|
|
1080
1125
|
returned results. If no record is found with these filters, returns None.
|
|
1081
1126
|
"""
|
|
1082
1127
|
# Query for all records that match the primary identifier.
|
|
1083
|
-
results: list[WrappedType] = self.query_models(wrapper_type, primary_identifier,
|
|
1128
|
+
results: list[WrappedType] | list[PyRecordModel] = self.query_models(wrapper_type, primary_identifier,
|
|
1129
|
+
[id_value])
|
|
1084
1130
|
|
|
1085
1131
|
# Find the one record, if any, that matches the secondary identifiers.
|
|
1086
|
-
unique_record: WrappedType | None = None
|
|
1132
|
+
unique_record: WrappedType | PyRecordModel | None = None
|
|
1087
1133
|
for result in results:
|
|
1088
1134
|
matches_all: bool = True
|
|
1089
1135
|
for field, value in secondary_identifiers.items():
|
|
@@ -1093,7 +1139,7 @@ class RecordHandler:
|
|
|
1093
1139
|
if matches_all:
|
|
1094
1140
|
# If a previous record in the results already matched all identifiers, then throw an exception.
|
|
1095
1141
|
if unique_record is not None:
|
|
1096
|
-
raise SapioException(f"More than one record of type {
|
|
1142
|
+
raise SapioException(f"More than one record of type {AliasUtil.to_data_type_name(wrapper_type)} "
|
|
1097
1143
|
f"encountered in system that matches all provided identifiers.")
|
|
1098
1144
|
unique_record = result
|
|
1099
1145
|
return unique_record
|