sapiopycommons 2025.3.6a453__py3-none-any.whl → 2025.3.10a455__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.

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