sapiopycommons 2024.8.19a305__py3-none-any.whl → 2024.8.26a307__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.

@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable
4
- from typing import Any
5
4
  from weakref import WeakValueDictionary
6
5
 
7
6
  from sapiopylib.rest.DataRecordManagerService import DataRecordManager
@@ -11,7 +10,6 @@ from sapiopylib.rest.pojo.DataRecord import DataRecord
11
10
  from sapiopylib.rest.pojo.DataRecordPaging import DataRecordPojoPageCriteria
12
11
  from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
13
12
  from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnBaseDataType
14
- from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
15
13
  from sapiopylib.rest.utils.autopaging import QueryDataRecordsAutoPager, QueryDataRecordByIdListAutoPager, \
16
14
  QueryAllRecordsOfTypeAutoPager
17
15
  from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
@@ -22,7 +20,8 @@ from sapiopylib.rest.utils.recordmodel.RelationshipPath import RelationshipPath,
22
20
  RelationshipNodeType
23
21
  from sapiopylib.rest.utils.recordmodel.ancestry import RecordModelAncestorManager
24
22
 
25
- from sapiopycommons.general.aliases import RecordModel, SapioRecord, FieldMap
23
+ from sapiopycommons.general.aliases import RecordModel, SapioRecord, FieldMap, FieldIdentifier, AliasUtil, \
24
+ FieldIdentifierMap, FieldValue, UserIdentifier, FieldIdentifierKey
26
25
  from sapiopycommons.general.custom_report_util import CustomReportUtil
27
26
  from sapiopycommons.general.exceptions import SapioException
28
27
 
@@ -42,11 +41,11 @@ class RecordHandler:
42
41
  __instances: WeakValueDictionary[SapioUser, RecordHandler] = WeakValueDictionary()
43
42
  __initialized: bool
44
43
 
45
- def __new__(cls, context: SapioWebhookContext | SapioUser):
44
+ def __new__(cls, context: UserIdentifier):
46
45
  """
47
46
  :param context: The current webhook context or a user object to send requests from.
48
47
  """
49
- user = context if isinstance(context, SapioUser) else context.user
48
+ user = AliasUtil.to_sapio_user(context)
50
49
  obj = cls.__instances.get(user)
51
50
  if not obj:
52
51
  obj = object.__new__(cls)
@@ -54,10 +53,11 @@ class RecordHandler:
54
53
  cls.__instances[user] = obj
55
54
  return obj
56
55
 
57
- def __init__(self, context: SapioWebhookContext | SapioUser):
56
+ def __init__(self, context: UserIdentifier):
58
57
  """
59
58
  :param context: The current webhook context or a user object to send requests from.
60
59
  """
60
+ self.user = AliasUtil.to_sapio_user(context)
61
61
  if self.__initialized:
62
62
  return
63
63
  self.__initialized = True
@@ -91,8 +91,8 @@ class RecordHandler:
91
91
  self.__verify_data_type(records, wrapper_type)
92
92
  return self.inst_man.add_existing_records_of_type(list(records), wrapper_type)
93
93
 
94
- def query_models(self, wrapper_type: type[WrappedType], field: str, value_list: Iterable[Any],
95
- page_limit: int | None = None) -> list[WrappedType]:
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]:
96
96
  """
97
97
  Shorthand for using the data record manager to query for a list of data records by field value
98
98
  and then converting the results into a list of record models.
@@ -100,14 +100,20 @@ class RecordHandler:
100
100
  :param wrapper_type: The record model wrapper to use.
101
101
  :param field: The field to query on.
102
102
  :param value_list: The values of the field to query on.
103
- :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
103
+ :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
104
+ only functions if you set a page size or the platform enforces a page size.
105
+ :param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
104
106
  :return: The record models for the queried records.
105
107
  """
106
- return self.query_models_with_criteria(wrapper_type, field, value_list, None, page_limit)[0]
108
+ criteria: DataRecordPojoPageCriteria | None = None
109
+ if page_size is not None:
110
+ criteria = DataRecordPojoPageCriteria(page_size=page_size)
111
+ return self.query_models_with_criteria(wrapper_type, field, value_list, criteria, page_limit)[0]
107
112
 
108
- def query_and_map_models(self, wrapper_type: type[WrappedType], field: str, value_list: Iterable[Any],
109
- page_limit: int | None = None, *, mapping_field: str | None = None) \
110
- -> dict[Any, list[WrappedType]]:
113
+ def query_and_map_models(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
114
+ value_list: Iterable[FieldValue], page_limit: int | None = None,
115
+ page_size: int | None = None, *, mapping_field: FieldIdentifier | None = None) \
116
+ -> dict[FieldValue, list[WrappedType]]:
111
117
  """
112
118
  Shorthand for using query_models to search for records given values on a specific field and then using
113
119
  map_by_field to turn the returned list into a dictionary mapping field values to records.
@@ -115,17 +121,21 @@ class RecordHandler:
115
121
  :param wrapper_type: The record model wrapper to use.
116
122
  :param field: The field to query and map on.
117
123
  :param value_list: The values of the field to query on.
118
- :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
124
+ :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
125
+ only functions if you set a page size or the platform enforces a page size.
126
+ :param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
119
127
  :param mapping_field: If provided, use this field to map against instead of the field that was queried on.
120
128
  :return: The record models for the queried records mapped by field values to the records with that value.
121
129
  """
122
130
  if mapping_field is None:
123
131
  mapping_field = field
124
- return self.map_by_field(self.query_models(wrapper_type, field, value_list, page_limit), mapping_field)
132
+ return self.map_by_field(self.query_models(wrapper_type, field, value_list, page_limit, page_size),
133
+ mapping_field)
125
134
 
126
- def query_and_unique_map_models(self, wrapper_type: type[WrappedType], field: str, value_list: Iterable[Any],
127
- page_limit: int | None = None, *, mapping_field: str | None = None) \
128
- -> dict[Any, WrappedType]:
135
+ def query_and_unique_map_models(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
136
+ value_list: Iterable[FieldValue], page_limit: int | None = None,
137
+ page_size: int | None = None, *, mapping_field: FieldIdentifier | None = None) \
138
+ -> dict[FieldValue, WrappedType]:
129
139
  """
130
140
  Shorthand for using query_models to search for records given values on a specific field and then using
131
141
  map_by_unique_field to turn the returned list into a dictionary mapping field values to records.
@@ -134,15 +144,19 @@ class RecordHandler:
134
144
  :param wrapper_type: The record model wrapper to use.
135
145
  :param field: The field to query and map on.
136
146
  :param value_list: The values of the field to query on.
137
- :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
147
+ :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
148
+ only functions if you set a page size or the platform enforces a page size.
149
+ :param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
138
150
  :param mapping_field: If provided, use this field to map against instead of the field that was queried on.
139
151
  :return: The record models for the queried records mapped by field values to the record with that value.
140
152
  """
141
153
  if mapping_field is None:
142
154
  mapping_field = field
143
- return self.map_by_unique_field(self.query_models(wrapper_type, field, value_list, page_limit), mapping_field)
155
+ return self.map_by_unique_field(self.query_models(wrapper_type, field, value_list, page_limit, page_size),
156
+ mapping_field)
144
157
 
145
- def query_models_with_criteria(self, wrapper_type: type[WrappedType], field: str, value_list: Iterable[Any],
158
+ def query_models_with_criteria(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
159
+ value_list: Iterable[FieldValue],
146
160
  paging_criteria: DataRecordPojoPageCriteria | None = None,
147
161
  page_limit: int | None = None) \
148
162
  -> tuple[list[WrappedType], DataRecordPojoPageCriteria]:
@@ -155,26 +169,33 @@ class RecordHandler:
155
169
  :param value_list: The values of the field to query on.
156
170
  :param paging_criteria: The paging criteria to start the query with.
157
171
  :param page_limit: The maximum number of pages to query from the starting criteria. If None, exhausts all
158
- possible pages.
172
+ possible pages. This parameter only functions if you set a page size in the paging criteria or the platform
173
+ enforces a page size.
159
174
  :return: The record models for the queried records and the final paging criteria.
160
175
  """
161
176
  dt: str = wrapper_type.get_wrapper_data_type_name()
177
+ field: str = AliasUtil.to_data_field_name(field)
162
178
  pager = QueryDataRecordsAutoPager(dt, field, list(value_list), self.user, paging_criteria)
163
179
  pager.max_page = page_limit
164
180
  return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
165
181
 
166
182
  def query_models_by_id(self, wrapper_type: type[WrappedType], ids: Iterable[int],
167
- page_limit: int | None = None) -> list[WrappedType]:
183
+ page_limit: int | None = None, page_size: int | None = None) -> list[WrappedType]:
168
184
  """
169
185
  Shorthand for using the data record manager to query for a list of data records by record ID
170
186
  and then converting the results into a list of record models.
171
187
 
172
188
  :param wrapper_type: The record model wrapper to use.
173
189
  :param ids: The list of record IDs to query.
174
- :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
190
+ :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
191
+ only functions if you set a page size or the platform enforces a page size.
192
+ :param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
175
193
  :return: The record models for the queried records.
176
194
  """
177
- return self.query_models_by_id_with_criteria(wrapper_type, ids, None, page_limit)[0]
195
+ criteria: DataRecordPojoPageCriteria | None = None
196
+ if page_size is not None:
197
+ criteria = DataRecordPojoPageCriteria(page_size=page_size)
198
+ return self.query_models_by_id_with_criteria(wrapper_type, ids, criteria, page_limit)[0]
178
199
 
179
200
  def query_models_by_id_with_criteria(self, wrapper_type: type[WrappedType], ids: Iterable[int],
180
201
  paging_criteria: DataRecordPojoPageCriteria | None = None,
@@ -188,7 +209,8 @@ class RecordHandler:
188
209
  :param ids: The list of record IDs to query.
189
210
  :param paging_criteria: The paging criteria to start the query with.
190
211
  :param page_limit: The maximum number of pages to query from the starting criteria. If None, exhausts all
191
- possible pages.
212
+ possible pages. This parameter only functions if you set a page size in the paging criteria or the platform
213
+ enforces a page size.
192
214
  :return: The record models for the queried records and the final paging criteria.
193
215
  """
194
216
  dt: str = wrapper_type.get_wrapper_data_type_name()
@@ -197,28 +219,37 @@ class RecordHandler:
197
219
  return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
198
220
 
199
221
  def query_models_by_id_and_map(self, wrapper_type: type[WrappedType], ids: Iterable[int],
200
- page_limit: int | None = None) -> dict[int, WrappedType]:
222
+ page_limit: int | None = None, page_size: int | None = None) \
223
+ -> dict[int, WrappedType]:
201
224
  """
202
225
  Shorthand for using the data record manager to query for a list of data records by record ID
203
226
  and then converting the results into a dictionary of record ID to the record model for that ID.
204
227
 
205
228
  :param wrapper_type: The record model wrapper to use.
206
229
  :param ids: The list of record IDs to query.
207
- :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
230
+ :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
231
+ only functions if you set a page size or the platform enforces a page size.
232
+ :param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
208
233
  :return: The record models for the queried records mapped in a dictionary by their record ID.
209
234
  """
210
- return {x.record_id: x for x in self.query_models_by_id(wrapper_type, ids, page_limit)}
235
+ return {x.record_id: x for x in self.query_models_by_id(wrapper_type, ids, page_limit, page_size)}
211
236
 
212
- def query_all_models(self, wrapper_type: type[WrappedType], page_limit: int | None = None) -> list[WrappedType]:
237
+ def query_all_models(self, wrapper_type: type[WrappedType], page_limit: int | None = None,
238
+ page_size: int | None = None) -> list[WrappedType]:
213
239
  """
214
240
  Shorthand for using the data record manager to query for all data records of a given type
215
241
  and then converting the results into a list of record models.
216
242
 
217
243
  :param wrapper_type: The record model wrapper to use.
218
- :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
244
+ :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages. This parameter
245
+ only functions if you set a page size or the platform enforces a page size.
246
+ :param page_size: The size of the pages to query. If None, the page size may be limited by the platform.
219
247
  :return: The record models for the queried records.
220
248
  """
221
- return self.query_all_models_with_criteria(wrapper_type, None, page_limit)[0]
249
+ criteria: DataRecordPojoPageCriteria | None = None
250
+ if page_size is not None:
251
+ criteria = DataRecordPojoPageCriteria(page_size=page_size)
252
+ return self.query_all_models_with_criteria(wrapper_type, criteria, page_limit)[0]
222
253
 
223
254
  def query_all_models_with_criteria(self, wrapper_type: type[WrappedType],
224
255
  paging_criteria: DataRecordPojoPageCriteria | None = None,
@@ -231,7 +262,8 @@ class RecordHandler:
231
262
  :param wrapper_type: The record model wrapper to use.
232
263
  :param paging_criteria: The paging criteria to start the query with.
233
264
  :param page_limit: The maximum number of pages to query from the starting criteria. If None, exhausts all
234
- possible pages.
265
+ possible pages. This parameter only functions if you set a page size in the paging criteria or the platform
266
+ enforces a page size.
235
267
  :return: The record models for the queried records and the final paging criteria.
236
268
  """
237
269
  dt: str = wrapper_type.get_wrapper_data_type_name()
@@ -241,7 +273,7 @@ class RecordHandler:
241
273
 
242
274
  def query_models_by_report(self, wrapper_type: type[WrappedType],
243
275
  report_name: str | RawReportTerm | CustomReportCriteria,
244
- filters: dict[str, Iterable[Any]] | None = None,
276
+ filters: dict[FieldIdentifierKey, Iterable[FieldValue]] | None = None,
245
277
  page_limit: int | None = None,
246
278
  page_size: int | None = None,
247
279
  page_number: int | None = None) -> list[WrappedType]:
@@ -269,11 +301,11 @@ class RecordHandler:
269
301
  :return: The record models for the queried records that matched the given report.
270
302
  """
271
303
  if isinstance(report_name, str):
272
- results: list[dict[str, Any]] = CustomReportUtil.run_system_report(self.user, report_name, filters,
273
- page_limit, page_size, page_number)
304
+ results: list[dict[str, FieldValue]] = CustomReportUtil.run_system_report(self.user, report_name, filters,
305
+ page_limit, page_size, page_number)
274
306
  elif isinstance(report_name, RawReportTerm):
275
- results: list[dict[str, Any]] = CustomReportUtil.run_quick_report(self.user, report_name, filters,
276
- page_limit, page_size, page_number)
307
+ results: list[dict[str, FieldValue]] = CustomReportUtil.run_quick_report(self.user, report_name, filters,
308
+ page_limit, page_size, page_number)
277
309
  elif isinstance(report_name, CustomReportCriteria):
278
310
  dt: str = wrapper_type.get_wrapper_data_type_name()
279
311
  # Ensure that the root data type is the one we're looking for.
@@ -284,8 +316,8 @@ class RecordHandler:
284
316
  # Enforce that the given custom report has a record ID column.
285
317
  if not any([x.data_type_name == dt and x.data_field_name == "RecordId" for x in report_name.column_list]):
286
318
  report_name.column_list.append(ReportColumn(dt, "RecordId", FieldType.LONG))
287
- results: list[dict[str, Any]] = CustomReportUtil.run_custom_report(self.user, report_name, filters,
288
- page_limit, page_size, page_number)
319
+ results: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(self.user, report_name, filters,
320
+ page_limit, page_size, page_number)
289
321
  else:
290
322
  raise SapioException("Unrecognized report object.")
291
323
 
@@ -314,7 +346,8 @@ class RecordHandler:
314
346
  """
315
347
  return self.inst_man.add_new_records_of_type(num, wrapper_type)
316
348
 
317
- def add_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldMap]) -> list[WrappedType]:
349
+ def add_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldIdentifierMap]) \
350
+ -> list[WrappedType]:
318
351
  """
319
352
  Shorthand for using the instance manager to add new models of the given type, and then initializing all those
320
353
  models with the given fields.
@@ -324,13 +357,14 @@ class RecordHandler:
324
357
  :return: The newly added record models with the provided fields set. The records will be in the same order as
325
358
  the fields in the fields list.
326
359
  """
360
+ fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(fields)
327
361
  models: list[WrappedType] = self.add_models(wrapper_type, len(fields))
328
362
  for model, field_list in zip(models, fields):
329
363
  model.set_field_values(field_list)
330
364
  return models
331
365
 
332
- def find_or_add_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: Any,
333
- secondary_identifiers: FieldMap | None = None) -> WrappedType:
366
+ def find_or_add_model(self, wrapper_type: type[WrappedType], primary_identifier: FieldIdentifier,
367
+ id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None) -> WrappedType:
334
368
  """
335
369
  Find a unique record that matches the given field values. If no such records exist, add a record model to the
336
370
  cache with the identifying fields set to the desired values. This record will be created in the system when
@@ -353,6 +387,8 @@ class RecordHandler:
353
387
  if secondary_identifiers is None:
354
388
  secondary_identifiers = {}
355
389
 
390
+ primary_identifier: str = AliasUtil.to_data_field_name(primary_identifier)
391
+ secondary_identifiers: FieldMap = AliasUtil.to_data_field_names_dict(secondary_identifiers)
356
392
  unique_record: WrappedType | None = self.__find_model(wrapper_type, primary_identifier, id_value,
357
393
  secondary_identifiers)
358
394
  # If a unique record matched the identifiers, return it.
@@ -379,7 +415,7 @@ class RecordHandler:
379
415
  dt: str = wrapper_type.get_wrapper_data_type_name()
380
416
  return self.wrap_models(self.dr_man.add_data_records(dt, num), wrapper_type)
381
417
 
382
- def create_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldMap]) \
418
+ def create_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldIdentifierMap]) \
383
419
  -> list[WrappedType]:
384
420
  """
385
421
  Shorthand for creating new records via the data record manager with field data to initialize the records with
@@ -393,10 +429,12 @@ class RecordHandler:
393
429
  :return: The newly created record models.
394
430
  """
395
431
  dt: str = wrapper_type.get_wrapper_data_type_name()
432
+ fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(fields)
396
433
  return self.wrap_models(self.dr_man.add_data_records_with_data(dt, fields), wrapper_type)
397
434
 
398
- def find_or_create_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: Any,
399
- secondary_identifiers: FieldMap | None = None) -> WrappedType:
435
+ def find_or_create_model(self, wrapper_type: type[WrappedType], primary_identifier: FieldIdentifier,
436
+ id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None) \
437
+ -> WrappedType:
400
438
  """
401
439
  Find a unique record that matches the given field values. If no such records exist, create one with the
402
440
  identifying fields set to the desired values. If more than one record with the identifying values exists,
@@ -420,6 +458,8 @@ class RecordHandler:
420
458
  if secondary_identifiers is None:
421
459
  secondary_identifiers = {}
422
460
 
461
+ primary_identifier: str = AliasUtil.to_data_field_name(primary_identifier)
462
+ secondary_identifiers: FieldMap = AliasUtil.to_data_field_names_dict(secondary_identifiers)
423
463
  unique_record: WrappedType | None = self.__find_model(wrapper_type, primary_identifier, id_value,
424
464
  secondary_identifiers)
425
465
  # If a unique record matched the identifiers, return it.
@@ -579,7 +619,7 @@ class RecordHandler:
579
619
  return by_children
580
620
 
581
621
  @staticmethod
582
- def map_to_forward_side_link(models: Iterable[WrappedRecordModel], field_name: str,
622
+ def map_to_forward_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
583
623
  side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, WrappedType]:
584
624
  """
585
625
  Map a list of record models to their forward side link. The forward side link must already be loaded.
@@ -590,13 +630,14 @@ class RecordHandler:
590
630
  :return: A dict[ModelType, SlideLink]. If an input model doesn't have a forward side link of the given type,
591
631
  then it will map to None.
592
632
  """
633
+ field_name: str = AliasUtil.to_data_field_name(field_name)
593
634
  return_dict: dict[WrappedRecordModel, WrappedType] = {}
594
635
  for model in models:
595
636
  return_dict[model] = model.get_forward_side_link(field_name, side_link_type)
596
637
  return return_dict
597
638
 
598
639
  @staticmethod
599
- def map_by_forward_side_links(models: Iterable[WrappedRecordModel], field_name: str,
640
+ def map_by_forward_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
600
641
  side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
601
642
  """
602
643
  Take a list of record models and map them by their forward side link. Essentially an inversion of
@@ -609,6 +650,7 @@ class RecordHandler:
609
650
  :return: A dict[SideLink, list[ModelType]]. If an input model doesn't have a forward side link of the given type
610
651
  pointing to it, then it will not be in the resulting dictionary.
611
652
  """
653
+ field_name: str = AliasUtil.to_data_field_name(field_name)
612
654
  to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
613
655
  .map_to_forward_side_link(models, field_name, side_link_type)
614
656
  by_side_link: dict[WrappedType, list[WrappedRecordModel]] = {}
@@ -619,7 +661,7 @@ class RecordHandler:
619
661
  return by_side_link
620
662
 
621
663
  @staticmethod
622
- def map_by_forward_side_link(models: Iterable[WrappedRecordModel], field_name: str,
664
+ def map_by_forward_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
623
665
  side_link_type: type[WrappedType]) -> dict[WrappedType, WrappedRecordModel]:
624
666
  """
625
667
  Take a list of record models and map them by their forward side link. Essentially an inversion of
@@ -632,6 +674,7 @@ class RecordHandler:
632
674
  :return: A dict[SideLink, ModelType]. If an input model doesn't have a forward side link of the given type
633
675
  pointing to it, then it will not be in the resulting dictionary.
634
676
  """
677
+ field_name: str = AliasUtil.to_data_field_name(field_name)
635
678
  to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
636
679
  .map_to_forward_side_link(models, field_name, side_link_type)
637
680
  by_side_link: dict[WrappedType, WrappedRecordModel] = {}
@@ -645,7 +688,7 @@ class RecordHandler:
645
688
  return by_side_link
646
689
 
647
690
  @staticmethod
648
- def map_to_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: str,
691
+ def map_to_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
649
692
  side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, list[WrappedType]]:
650
693
  """
651
694
  Map a list of record models to a list reverse side links of a given type. The reverse side links must already
@@ -658,13 +701,14 @@ class RecordHandler:
658
701
  :return: A dict[ModelType, list[SideLink]]. If an input model doesn't have reverse side links of the given type,
659
702
  then it will map to an empty list.
660
703
  """
704
+ field_name: str = AliasUtil.to_data_field_name(field_name)
661
705
  return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
662
706
  for model in models:
663
707
  return_dict[model] = model.get_reverse_side_link(field_name, side_link_type)
664
708
  return return_dict
665
709
 
666
710
  @staticmethod
667
- def map_to_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: str,
711
+ def map_to_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
668
712
  side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, WrappedType]:
669
713
  """
670
714
  Map a list of record models to the reverse side link of a given type. If a given record has more than one
@@ -677,6 +721,7 @@ class RecordHandler:
677
721
  :return: A dict[ModelType, SideLink]. If an input model doesn't have reverse side links of the given type,
678
722
  then it will map to None.
679
723
  """
724
+ field_name: str = AliasUtil.to_data_field_name(field_name)
680
725
  return_dict: dict[WrappedRecordModel, WrappedType] = {}
681
726
  for model in models:
682
727
  links: list[WrappedType] = model.get_reverse_side_link(field_name, side_link_type)
@@ -687,7 +732,7 @@ class RecordHandler:
687
732
  return return_dict
688
733
 
689
734
  @staticmethod
690
- def map_by_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: str,
735
+ def map_by_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
691
736
  side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
692
737
  """
693
738
  Take a list of record models and map them by their reverse side links. Essentially an inversion of
@@ -701,6 +746,7 @@ class RecordHandler:
701
746
  :return: A dict[SideLink, list[ModelType]]. If an input model doesn't have reverse side links of the given type
702
747
  pointing to it, then it will not be in the resulting dictionary.
703
748
  """
749
+ field_name: str = AliasUtil.to_data_field_name(field_name)
704
750
  to_side_links: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler\
705
751
  .map_to_reverse_side_links(models, field_name, side_link_type)
706
752
  by_side_links: dict[WrappedType, list[WrappedRecordModel]] = {}
@@ -710,7 +756,7 @@ class RecordHandler:
710
756
  return by_side_links
711
757
 
712
758
  @staticmethod
713
- def map_by_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: str,
759
+ def map_by_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
714
760
  side_link_type: type[WrappedType]) -> dict[WrappedType, WrappedRecordModel]:
715
761
  """
716
762
  Take a list of record models and map them by their reverse side link. Essentially an inversion of
@@ -724,6 +770,7 @@ class RecordHandler:
724
770
  :return: A dict[SideLink, ModelType]. If an input model doesn't have a reverse side link of the given type
725
771
  pointing to it, then it will not be in the resulting dictionary.
726
772
  """
773
+ field_name: str = AliasUtil.to_data_field_name(field_name)
727
774
  to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
728
775
  .map_to_reverse_side_link(models, field_name, side_link_type)
729
776
  by_side_link: dict[WrappedType, WrappedRecordModel] = {}
@@ -750,7 +797,8 @@ class RecordHandler:
750
797
  return ret_dict
751
798
 
752
799
  @staticmethod
753
- def map_by_field(models: Iterable[SapioRecord], field_name: str) -> dict[Any, list[SapioRecord]]:
800
+ def map_by_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) \
801
+ -> dict[FieldValue, list[SapioRecord]]:
754
802
  """
755
803
  Map the given records by one of their fields. If any two records share the same field value, they'll appear in
756
804
  the same value list.
@@ -759,14 +807,16 @@ class RecordHandler:
759
807
  :param field_name: The field name to map against.
760
808
  :return: A dict mapping field values to the records with that value.
761
809
  """
762
- ret_dict: dict[Any, list[SapioRecord]] = {}
810
+ field_name: str = AliasUtil.to_data_field_name(field_name)
811
+ ret_dict: dict[FieldValue, list[SapioRecord]] = {}
763
812
  for model in models:
764
- val: Any = model.get_field_value(field_name)
813
+ val: FieldValue = model.get_field_value(field_name)
765
814
  ret_dict.setdefault(val, []).append(model)
766
815
  return ret_dict
767
816
 
768
817
  @staticmethod
769
- def map_by_unique_field(models: Iterable[SapioRecord], field_name: str) -> dict[Any, SapioRecord]:
818
+ def map_by_unique_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) \
819
+ -> dict[FieldValue, SapioRecord]:
770
820
  """
771
821
  Uniquely map the given records by one of their fields. If any two records share the same field value, throws
772
822
  an exception.
@@ -775,16 +825,17 @@ class RecordHandler:
775
825
  :param field_name: The field name to map against.
776
826
  :return: A dict mapping field values to the record with that value.
777
827
  """
778
- ret_dict: dict[Any, SapioRecord] = {}
828
+ field_name: str = AliasUtil.to_data_field_name(field_name)
829
+ ret_dict: dict[FieldValue, SapioRecord] = {}
779
830
  for model in models:
780
- val: Any = model.get_field_value(field_name)
831
+ val: FieldValue = model.get_field_value(field_name)
781
832
  if val in ret_dict:
782
833
  raise SapioException(f"Value {val} encountered more than once in models list.")
783
834
  ret_dict.update({val: model})
784
835
  return ret_dict
785
836
 
786
837
  @staticmethod
787
- def sum_of_field(models: Iterable[SapioRecord], field_name: str) -> float:
838
+ def sum_of_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) -> float:
788
839
  """
789
840
  Sum up the numeric value of a given field across all input models. Excepts that all given models have a value.
790
841
  If the field is an integer field, the value will be converted to a float.
@@ -793,13 +844,14 @@ class RecordHandler:
793
844
  :param field_name: The name of the numeric field to sum.
794
845
  :return: The sum of the field values for the collection of models.
795
846
  """
847
+ field_name: str = AliasUtil.to_data_field_name(field_name)
796
848
  field_sum: float = 0
797
849
  for model in models:
798
850
  field_sum += float(model.get_field_value(field_name))
799
851
  return field_sum
800
852
 
801
853
  @staticmethod
802
- def mean_of_field(models: Iterable[SapioRecord], field_name: str) -> float:
854
+ def mean_of_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) -> float:
803
855
  """
804
856
  Calculate the mean of the numeric value of a given field across all input models. Excepts that all given models
805
857
  have a value. If the field is an integer field, the value will be converted to a float.
@@ -840,8 +892,8 @@ class RecordHandler:
840
892
  return oldest
841
893
 
842
894
  @staticmethod
843
- def values_to_field_maps(field_name: str, values: Iterable[Any], existing_fields: list[FieldMap] | None = None) \
844
- -> list[FieldMap]:
895
+ def values_to_field_maps(field_name: FieldIdentifier, values: Iterable[FieldValue],
896
+ existing_fields: list[FieldIdentifier] | None = None) -> list[FieldMap]:
845
897
  """
846
898
  Add a list of values for a specific field to a list of dictionaries pairing each value to that field name.
847
899
 
@@ -852,6 +904,8 @@ class RecordHandler:
852
904
  :return: A fields map list that contains the given values mapped by the given field name.
853
905
  """
854
906
  # Update the existing fields map list if one is given.
907
+ field_name: str = AliasUtil.to_data_field_name(field_name)
908
+ existing_fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(existing_fields)
855
909
  if existing_fields:
856
910
  values = list(values)
857
911
  # The number of new values must match the length of the existing fields list.
@@ -1016,8 +1070,8 @@ class RecordHandler:
1016
1070
  ret_dict.update({model: self.inst_man.wrap(current[0], wrapper_type) if current else None})
1017
1071
  return ret_dict
1018
1072
 
1019
- def __find_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: Any,
1020
- secondary_identifiers: FieldMap | None = None) -> WrappedType | None:
1073
+ def __find_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: FieldValue,
1074
+ secondary_identifiers: FieldIdentifierMap | None = None) -> WrappedType | None:
1021
1075
  """
1022
1076
  Find a record from the system that matches the given field values. The primary identifier and value is used
1023
1077
  to query for the record, then the secondary identifiers may be optionally provided to further filter the
@@ -9,7 +9,7 @@ from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
9
9
  from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelManager, RecordModelInstanceManager
10
10
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
11
11
 
12
- from sapiopycommons.general.aliases import FieldMap
12
+ from sapiopycommons.general.aliases import FieldMap, AliasUtil, DataTypeIdentifier
13
13
  from sapiopycommons.general.exceptions import SapioException
14
14
 
15
15
 
@@ -125,7 +125,7 @@ class ElnRuleHandler:
125
125
  """
126
126
  return list(self.__entry_to_field_maps.keys())
127
127
 
128
- def get_records(self, data_type: str, entry: str | None = None) -> list[DataRecord]:
128
+ def get_records(self, data_type: DataTypeIdentifier, entry: str | None = None) -> list[DataRecord]:
129
129
  """
130
130
  Get records from the cached context with the given data type. Capable of being filtered to searching within
131
131
  the context of an entry name. If the given data type or entry does not exist in the context,
@@ -136,11 +136,12 @@ class ElnRuleHandler:
136
136
  type from every entry. If an entry is provided, but it does not exist in the context, returns an empty list.
137
137
  :return: The records from the context that match the input parameters.
138
138
  """
139
+ data_type: str = AliasUtil.to_data_type_name(data_type)
139
140
  records: dict[str, set[DataRecord]] = self.__entry_to_records.get(entry, {}) if entry else self.__records
140
141
  return list(records.get(data_type, []))
141
142
 
142
143
  # FR-46701: Add functions to the rule handlers for accessing the field maps of inaccessible records in the context.
143
- def get_field_maps(self, data_type: str, entry: str | None = None) -> list[FieldMap]:
144
+ def get_field_maps(self, data_type: DataTypeIdentifier, entry: str | None = None) -> list[FieldMap]:
144
145
  """
145
146
  Get field maps from the cached context with the given data type. Capable of being filtered to searching within
146
147
  the context of an entry name. If the given data type or entry does not exist in the context,
@@ -156,6 +157,7 @@ class ElnRuleHandler:
156
157
  list.
157
158
  :return: The field maps from the context that match the input parameters.
158
159
  """
160
+ data_type: str = AliasUtil.to_data_type_name(data_type)
159
161
  field_maps: dict[str, dict[int, FieldMap]] = self.__entry_to_field_maps.get(entry, {}) if entry else self.__field_maps
160
162
  return list(field_maps.get(data_type, {}).values())
161
163
 
@@ -9,7 +9,7 @@ from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
9
9
  from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelManager, RecordModelInstanceManager
10
10
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
11
11
 
12
- from sapiopycommons.general.aliases import FieldMap
12
+ from sapiopycommons.general.aliases import FieldMap, DataTypeIdentifier, AliasUtil
13
13
  from sapiopycommons.general.exceptions import SapioException
14
14
 
15
15
 
@@ -121,7 +121,7 @@ class OnSaveRuleHandler:
121
121
  """
122
122
  return list(self.__base_id_to_field_maps.keys())
123
123
 
124
- def get_records(self, data_type: str, record_id: int | None = None) -> list[DataRecord]:
124
+ def get_records(self, data_type: DataTypeIdentifier, record_id: int | None = None) -> list[DataRecord]:
125
125
  """
126
126
  Get records from the cached context with the given data type. Capable of being filtered to searching within
127
127
  the context of a record ID. If the given data type or record ID does not exist in the context,
@@ -132,11 +132,12 @@ class OnSaveRuleHandler:
132
132
  data type from every ID. If an ID is provided, but it does not exist in the context, returns an empty list.
133
133
  :return: The records from the context that match the input parameters.
134
134
  """
135
+ data_type: str = AliasUtil.to_data_type_name(data_type)
135
136
  records: dict[str, set[DataRecord]] = self.__base_id_to_records.get(record_id, {}) if record_id else self.__records
136
137
  return list(records.get(data_type, []))
137
138
 
138
139
  # FR-46701: Add functions to the rule handlers for accessing the field maps of inaccessible records in the context.
139
- def get_field_maps(self, data_type: str, record_id: int | None = None) -> list[FieldMap]:
140
+ def get_field_maps(self, data_type: DataTypeIdentifier, record_id: int | None = None) -> list[FieldMap]:
140
141
  """
141
142
  Get field maps from the cached context with the given data type. Capable of being filtered to searching within
142
143
  the context of a record ID. If the given data type or record ID does not exist in the context,
@@ -152,6 +153,7 @@ class OnSaveRuleHandler:
152
153
  list.
153
154
  :return: The field maps from the context that match the input parameters.
154
155
  """
156
+ data_type: str = AliasUtil.to_data_type_name(data_type)
155
157
  field_maps: dict[str, dict[int, FieldMap]] = self.__base_id_to_field_maps.get(record_id, {}) if record_id else self.__field_maps
156
158
  return list(field_maps.get(data_type, {}).values())
157
159
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2024.8.19a305
3
+ Version: 2024.8.26a307
4
4
  Summary: Official Sapio Python API Utilities Package
5
5
  Project-URL: Homepage, https://github.com/sapiosciences
6
6
  Author-email: Jonathan Steck <jsteck@sapiosciences.com>, Yechen Qiao <yqiao@sapiosciences.com>