sapiopycommons 2024.8.19a305__py3-none-any.whl → 2024.8.20a306__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,12 +1,12 @@
1
1
  from sapiopylib.rest.User import SapioUser
2
- from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
3
2
 
4
- from sapiopycommons.general.aliases import RecordIdentifier, AliasUtil, ExperimentIdentifier
3
+ from sapiopycommons.general.aliases import RecordIdentifier, AliasUtil, ExperimentIdentifier, DataTypeIdentifier, \
4
+ UserIdentifier
5
5
 
6
6
 
7
7
  class ProcessTracking:
8
8
  @staticmethod
9
- def assign_to_process(context: SapioWebhookContext | SapioUser, data_type: str, records: list[RecordIdentifier],
9
+ def assign_to_process(context: UserIdentifier, data_type: DataTypeIdentifier, records: list[RecordIdentifier],
10
10
  process_name: str, step_number: int | None = None, branch_id: int | None = None,
11
11
  request: RecordIdentifier | None = None) -> None:
12
12
  """
@@ -27,19 +27,19 @@ class ProcessTracking:
27
27
  """
28
28
  sub_path = '/ext/process-tracking/assign-to-process'
29
29
  payload = {
30
- "data-type-name": data_type,
30
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
31
31
  "record-ids": AliasUtil.to_record_ids(records),
32
32
  "process-name": process_name,
33
33
  "step-number": step_number,
34
34
  "branch-id": branch_id,
35
35
  "request-record-id": AliasUtil.to_record_ids([request])[0] if request is not None else None
36
36
  }
37
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
37
+ user: SapioUser = AliasUtil.to_sapio_user(context)
38
38
  response = user.post(sub_path, payload=payload)
39
39
  user.raise_for_status(response)
40
40
 
41
41
  @staticmethod
42
- def begin_protocol(context: SapioWebhookContext | SapioUser, data_type: str, records: list[RecordIdentifier],
42
+ def begin_protocol(context: UserIdentifier, data_type: DataTypeIdentifier, records: list[RecordIdentifier],
43
43
  experiment: ExperimentIdentifier) -> None:
44
44
  """
45
45
  Begin the assigned processes of the given tracked records as the given experiment. This sets the status of the
@@ -54,16 +54,16 @@ class ProcessTracking:
54
54
  """
55
55
  sub_path = '/ext/process-tracking/begin-protocol'
56
56
  payload = {
57
- "data-type-name": data_type,
57
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
58
58
  "record-ids": AliasUtil.to_record_ids(records),
59
59
  "experiment-id": AliasUtil.to_notebook_id(experiment),
60
60
  }
61
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
61
+ user: SapioUser = AliasUtil.to_sapio_user(context)
62
62
  response = user.post(sub_path, payload=payload)
63
63
  user.raise_for_status(response)
64
64
 
65
65
  @staticmethod
66
- def complete_protocol(context: SapioWebhookContext | SapioUser, data_type: str, records: list[RecordIdentifier],
66
+ def complete_protocol(context: UserIdentifier, data_type: DataTypeIdentifier, records: list[RecordIdentifier],
67
67
  experiment: ExperimentIdentifier) -> None:
68
68
  """
69
69
  Complete the current step that the given tracked records are at given the experiment.
@@ -80,16 +80,16 @@ class ProcessTracking:
80
80
  """
81
81
  sub_path = '/ext/process-tracking/complete-protocol'
82
82
  payload = {
83
- "data-type-name": data_type,
83
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
84
84
  "record-ids": AliasUtil.to_record_ids(records),
85
85
  "experiment-id": AliasUtil.to_notebook_id(experiment),
86
86
  }
87
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
87
+ user: SapioUser = AliasUtil.to_sapio_user(context)
88
88
  response = user.post(sub_path, payload=payload)
89
89
  user.raise_for_status(response)
90
90
 
91
91
  @staticmethod
92
- def fail(context: SapioWebhookContext | SapioUser, data_type: str, records: list[RecordIdentifier],
92
+ def fail(context: UserIdentifier, data_type: DataTypeIdentifier, records: list[RecordIdentifier],
93
93
  experiment: ExperimentIdentifier) -> None:
94
94
  """
95
95
  Fail the assigned processes of the given tracked records, changing their statuses to "Failed -". The tracked
@@ -103,16 +103,16 @@ class ProcessTracking:
103
103
  """
104
104
  sub_path = '/ext/process-tracking/fail'
105
105
  payload = {
106
- "data-type-name": data_type,
106
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
107
107
  "record-ids": AliasUtil.to_record_ids(records),
108
108
  "experiment-id": AliasUtil.to_notebook_id(experiment),
109
109
  }
110
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
110
+ user: SapioUser = AliasUtil.to_sapio_user(context)
111
111
  response = user.post(sub_path, payload=payload)
112
112
  user.raise_for_status(response)
113
113
 
114
114
  @staticmethod
115
- def promote_to_next_by_experiment(context: SapioWebhookContext | SapioUser, data_type: str,
115
+ def promote_to_next_by_experiment(context: UserIdentifier, data_type: DataTypeIdentifier,
116
116
  records: list[RecordIdentifier], experiment: ExperimentIdentifier) -> None:
117
117
  """
118
118
  Promote the status of the given tracked records to the next status in their process using an experiment.
@@ -129,16 +129,16 @@ class ProcessTracking:
129
129
  """
130
130
  sub_path = '/ext/process-tracking/promote-status-to-next'
131
131
  payload = {
132
- "data-type-name": data_type,
132
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
133
133
  "record-ids": AliasUtil.to_record_ids(records),
134
134
  "experiment-id": AliasUtil.to_notebook_id(experiment),
135
135
  }
136
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
136
+ user: SapioUser = AliasUtil.to_sapio_user(context)
137
137
  response = user.post(sub_path, payload=payload)
138
138
  user.raise_for_status(response)
139
139
 
140
140
  @staticmethod
141
- def promote_to_next_by_step(context: SapioWebhookContext | SapioUser, data_type: str,
141
+ def promote_to_next_by_step(context: UserIdentifier, data_type: DataTypeIdentifier,
142
142
  records: list[RecordIdentifier], process_name: str, step_number: int,
143
143
  branch_id: int | None = None) -> None:
144
144
  """
@@ -159,7 +159,7 @@ class ProcessTracking:
159
159
  """
160
160
  sub_path = '/ext/process-tracking/promote-status-to-next'
161
161
  payload = {
162
- "data-type-name": data_type,
162
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
163
163
  "record-ids": AliasUtil.to_record_ids(records),
164
164
  "current-process-status": {
165
165
  "process-name": process_name,
@@ -167,12 +167,12 @@ class ProcessTracking:
167
167
  "branch-id": branch_id
168
168
  }
169
169
  }
170
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
170
+ user: SapioUser = AliasUtil.to_sapio_user(context)
171
171
  response = user.post(sub_path, payload=payload)
172
172
  user.raise_for_status(response)
173
173
 
174
174
  @staticmethod
175
- def reprocess(context: SapioWebhookContext | SapioUser, records: list[RecordIdentifier]) -> None:
175
+ def reprocess(context: UserIdentifier, records: list[RecordIdentifier]) -> None:
176
176
  """
177
177
  Reprocess tracked records to a previous step in their process. Reprocessing is controlled by ReturnPoint records
178
178
  which are children of the AssignedProcess on the tracked records. Creates a new AssignedProcess record for the
@@ -187,6 +187,6 @@ class ProcessTracking:
187
187
  payload = {
188
188
  "record-ids": AliasUtil.to_record_ids(records)
189
189
  }
190
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
190
+ user: SapioUser = AliasUtil.to_sapio_user(context)
191
191
  response = user.post(sub_path, payload=payload)
192
192
  user.raise_for_status(response)
@@ -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,7 +91,7 @@ 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],
94
+ def query_models(self, wrapper_type: type[WrappedType], field: FieldIdentifier, value_list: Iterable[FieldValue],
95
95
  page_limit: 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
@@ -105,9 +105,9 @@ class RecordHandler:
105
105
  """
106
106
  return self.query_models_with_criteria(wrapper_type, field, value_list, None, page_limit)[0]
107
107
 
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]]:
108
+ def query_and_map_models(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
109
+ value_list: Iterable[FieldValue], page_limit: int | None = None,
110
+ *, mapping_field: FieldIdentifier | None = None) -> dict[FieldValue, list[WrappedType]]:
111
111
  """
112
112
  Shorthand for using query_models to search for records given values on a specific field and then using
113
113
  map_by_field to turn the returned list into a dictionary mapping field values to records.
@@ -123,9 +123,10 @@ class RecordHandler:
123
123
  mapping_field = field
124
124
  return self.map_by_field(self.query_models(wrapper_type, field, value_list, page_limit), mapping_field)
125
125
 
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]:
126
+ def query_and_unique_map_models(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
127
+ value_list: Iterable[FieldValue], page_limit: int | None = None,
128
+ *, mapping_field: FieldIdentifier | None = None) \
129
+ -> dict[FieldValue, WrappedType]:
129
130
  """
130
131
  Shorthand for using query_models to search for records given values on a specific field and then using
131
132
  map_by_unique_field to turn the returned list into a dictionary mapping field values to records.
@@ -142,7 +143,8 @@ class RecordHandler:
142
143
  mapping_field = field
143
144
  return self.map_by_unique_field(self.query_models(wrapper_type, field, value_list, page_limit), mapping_field)
144
145
 
145
- def query_models_with_criteria(self, wrapper_type: type[WrappedType], field: str, value_list: Iterable[Any],
146
+ def query_models_with_criteria(self, wrapper_type: type[WrappedType], field: FieldIdentifier,
147
+ value_list: Iterable[FieldValue],
146
148
  paging_criteria: DataRecordPojoPageCriteria | None = None,
147
149
  page_limit: int | None = None) \
148
150
  -> tuple[list[WrappedType], DataRecordPojoPageCriteria]:
@@ -159,6 +161,7 @@ class RecordHandler:
159
161
  :return: The record models for the queried records and the final paging criteria.
160
162
  """
161
163
  dt: str = wrapper_type.get_wrapper_data_type_name()
164
+ field: str = AliasUtil.to_data_field_name(field)
162
165
  pager = QueryDataRecordsAutoPager(dt, field, list(value_list), self.user, paging_criteria)
163
166
  pager.max_page = page_limit
164
167
  return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
@@ -241,7 +244,7 @@ class RecordHandler:
241
244
 
242
245
  def query_models_by_report(self, wrapper_type: type[WrappedType],
243
246
  report_name: str | RawReportTerm | CustomReportCriteria,
244
- filters: dict[str, Iterable[Any]] | None = None,
247
+ filters: dict[FieldIdentifierKey, Iterable[FieldValue]] | None = None,
245
248
  page_limit: int | None = None,
246
249
  page_size: int | None = None,
247
250
  page_number: int | None = None) -> list[WrappedType]:
@@ -269,11 +272,11 @@ class RecordHandler:
269
272
  :return: The record models for the queried records that matched the given report.
270
273
  """
271
274
  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)
275
+ results: list[dict[str, FieldValue]] = CustomReportUtil.run_system_report(self.user, report_name, filters,
276
+ page_limit, page_size, page_number)
274
277
  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)
278
+ results: list[dict[str, FieldValue]] = CustomReportUtil.run_quick_report(self.user, report_name, filters,
279
+ page_limit, page_size, page_number)
277
280
  elif isinstance(report_name, CustomReportCriteria):
278
281
  dt: str = wrapper_type.get_wrapper_data_type_name()
279
282
  # Ensure that the root data type is the one we're looking for.
@@ -284,8 +287,8 @@ class RecordHandler:
284
287
  # Enforce that the given custom report has a record ID column.
285
288
  if not any([x.data_type_name == dt and x.data_field_name == "RecordId" for x in report_name.column_list]):
286
289
  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)
290
+ results: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(self.user, report_name, filters,
291
+ page_limit, page_size, page_number)
289
292
  else:
290
293
  raise SapioException("Unrecognized report object.")
291
294
 
@@ -314,7 +317,8 @@ class RecordHandler:
314
317
  """
315
318
  return self.inst_man.add_new_records_of_type(num, wrapper_type)
316
319
 
317
- def add_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldMap]) -> list[WrappedType]:
320
+ def add_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldIdentifierMap]) \
321
+ -> list[WrappedType]:
318
322
  """
319
323
  Shorthand for using the instance manager to add new models of the given type, and then initializing all those
320
324
  models with the given fields.
@@ -324,13 +328,14 @@ class RecordHandler:
324
328
  :return: The newly added record models with the provided fields set. The records will be in the same order as
325
329
  the fields in the fields list.
326
330
  """
331
+ fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(fields)
327
332
  models: list[WrappedType] = self.add_models(wrapper_type, len(fields))
328
333
  for model, field_list in zip(models, fields):
329
334
  model.set_field_values(field_list)
330
335
  return models
331
336
 
332
- def find_or_add_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: Any,
333
- secondary_identifiers: FieldMap | None = None) -> WrappedType:
337
+ def find_or_add_model(self, wrapper_type: type[WrappedType], primary_identifier: FieldIdentifier,
338
+ id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None) -> WrappedType:
334
339
  """
335
340
  Find a unique record that matches the given field values. If no such records exist, add a record model to the
336
341
  cache with the identifying fields set to the desired values. This record will be created in the system when
@@ -353,6 +358,8 @@ class RecordHandler:
353
358
  if secondary_identifiers is None:
354
359
  secondary_identifiers = {}
355
360
 
361
+ primary_identifier: str = AliasUtil.to_data_field_name(primary_identifier)
362
+ secondary_identifiers: FieldMap = AliasUtil.to_data_field_names_dict(secondary_identifiers)
356
363
  unique_record: WrappedType | None = self.__find_model(wrapper_type, primary_identifier, id_value,
357
364
  secondary_identifiers)
358
365
  # If a unique record matched the identifiers, return it.
@@ -379,7 +386,7 @@ class RecordHandler:
379
386
  dt: str = wrapper_type.get_wrapper_data_type_name()
380
387
  return self.wrap_models(self.dr_man.add_data_records(dt, num), wrapper_type)
381
388
 
382
- def create_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldMap]) \
389
+ def create_models_with_data(self, wrapper_type: type[WrappedType], fields: list[FieldIdentifierMap]) \
383
390
  -> list[WrappedType]:
384
391
  """
385
392
  Shorthand for creating new records via the data record manager with field data to initialize the records with
@@ -393,10 +400,12 @@ class RecordHandler:
393
400
  :return: The newly created record models.
394
401
  """
395
402
  dt: str = wrapper_type.get_wrapper_data_type_name()
403
+ fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(fields)
396
404
  return self.wrap_models(self.dr_man.add_data_records_with_data(dt, fields), wrapper_type)
397
405
 
398
- def find_or_create_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: Any,
399
- secondary_identifiers: FieldMap | None = None) -> WrappedType:
406
+ def find_or_create_model(self, wrapper_type: type[WrappedType], primary_identifier: FieldIdentifier,
407
+ id_value: FieldValue, secondary_identifiers: FieldIdentifierMap | None = None) \
408
+ -> WrappedType:
400
409
  """
401
410
  Find a unique record that matches the given field values. If no such records exist, create one with the
402
411
  identifying fields set to the desired values. If more than one record with the identifying values exists,
@@ -420,6 +429,8 @@ class RecordHandler:
420
429
  if secondary_identifiers is None:
421
430
  secondary_identifiers = {}
422
431
 
432
+ primary_identifier: str = AliasUtil.to_data_field_name(primary_identifier)
433
+ secondary_identifiers: FieldMap = AliasUtil.to_data_field_names_dict(secondary_identifiers)
423
434
  unique_record: WrappedType | None = self.__find_model(wrapper_type, primary_identifier, id_value,
424
435
  secondary_identifiers)
425
436
  # If a unique record matched the identifiers, return it.
@@ -579,7 +590,7 @@ class RecordHandler:
579
590
  return by_children
580
591
 
581
592
  @staticmethod
582
- def map_to_forward_side_link(models: Iterable[WrappedRecordModel], field_name: str,
593
+ def map_to_forward_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
583
594
  side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, WrappedType]:
584
595
  """
585
596
  Map a list of record models to their forward side link. The forward side link must already be loaded.
@@ -590,13 +601,14 @@ class RecordHandler:
590
601
  :return: A dict[ModelType, SlideLink]. If an input model doesn't have a forward side link of the given type,
591
602
  then it will map to None.
592
603
  """
604
+ field_name: str = AliasUtil.to_data_field_name(field_name)
593
605
  return_dict: dict[WrappedRecordModel, WrappedType] = {}
594
606
  for model in models:
595
607
  return_dict[model] = model.get_forward_side_link(field_name, side_link_type)
596
608
  return return_dict
597
609
 
598
610
  @staticmethod
599
- def map_by_forward_side_links(models: Iterable[WrappedRecordModel], field_name: str,
611
+ def map_by_forward_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
600
612
  side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
601
613
  """
602
614
  Take a list of record models and map them by their forward side link. Essentially an inversion of
@@ -609,6 +621,7 @@ class RecordHandler:
609
621
  :return: A dict[SideLink, list[ModelType]]. If an input model doesn't have a forward side link of the given type
610
622
  pointing to it, then it will not be in the resulting dictionary.
611
623
  """
624
+ field_name: str = AliasUtil.to_data_field_name(field_name)
612
625
  to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
613
626
  .map_to_forward_side_link(models, field_name, side_link_type)
614
627
  by_side_link: dict[WrappedType, list[WrappedRecordModel]] = {}
@@ -619,7 +632,7 @@ class RecordHandler:
619
632
  return by_side_link
620
633
 
621
634
  @staticmethod
622
- def map_by_forward_side_link(models: Iterable[WrappedRecordModel], field_name: str,
635
+ def map_by_forward_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
623
636
  side_link_type: type[WrappedType]) -> dict[WrappedType, WrappedRecordModel]:
624
637
  """
625
638
  Take a list of record models and map them by their forward side link. Essentially an inversion of
@@ -632,6 +645,7 @@ class RecordHandler:
632
645
  :return: A dict[SideLink, ModelType]. If an input model doesn't have a forward side link of the given type
633
646
  pointing to it, then it will not be in the resulting dictionary.
634
647
  """
648
+ field_name: str = AliasUtil.to_data_field_name(field_name)
635
649
  to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
636
650
  .map_to_forward_side_link(models, field_name, side_link_type)
637
651
  by_side_link: dict[WrappedType, WrappedRecordModel] = {}
@@ -645,7 +659,7 @@ class RecordHandler:
645
659
  return by_side_link
646
660
 
647
661
  @staticmethod
648
- def map_to_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: str,
662
+ def map_to_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
649
663
  side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, list[WrappedType]]:
650
664
  """
651
665
  Map a list of record models to a list reverse side links of a given type. The reverse side links must already
@@ -658,13 +672,14 @@ class RecordHandler:
658
672
  :return: A dict[ModelType, list[SideLink]]. If an input model doesn't have reverse side links of the given type,
659
673
  then it will map to an empty list.
660
674
  """
675
+ field_name: str = AliasUtil.to_data_field_name(field_name)
661
676
  return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
662
677
  for model in models:
663
678
  return_dict[model] = model.get_reverse_side_link(field_name, side_link_type)
664
679
  return return_dict
665
680
 
666
681
  @staticmethod
667
- def map_to_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: str,
682
+ def map_to_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
668
683
  side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, WrappedType]:
669
684
  """
670
685
  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 +692,7 @@ class RecordHandler:
677
692
  :return: A dict[ModelType, SideLink]. If an input model doesn't have reverse side links of the given type,
678
693
  then it will map to None.
679
694
  """
695
+ field_name: str = AliasUtil.to_data_field_name(field_name)
680
696
  return_dict: dict[WrappedRecordModel, WrappedType] = {}
681
697
  for model in models:
682
698
  links: list[WrappedType] = model.get_reverse_side_link(field_name, side_link_type)
@@ -687,7 +703,7 @@ class RecordHandler:
687
703
  return return_dict
688
704
 
689
705
  @staticmethod
690
- def map_by_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: str,
706
+ def map_by_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
691
707
  side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
692
708
  """
693
709
  Take a list of record models and map them by their reverse side links. Essentially an inversion of
@@ -701,6 +717,7 @@ class RecordHandler:
701
717
  :return: A dict[SideLink, list[ModelType]]. If an input model doesn't have reverse side links of the given type
702
718
  pointing to it, then it will not be in the resulting dictionary.
703
719
  """
720
+ field_name: str = AliasUtil.to_data_field_name(field_name)
704
721
  to_side_links: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler\
705
722
  .map_to_reverse_side_links(models, field_name, side_link_type)
706
723
  by_side_links: dict[WrappedType, list[WrappedRecordModel]] = {}
@@ -710,7 +727,7 @@ class RecordHandler:
710
727
  return by_side_links
711
728
 
712
729
  @staticmethod
713
- def map_by_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: str,
730
+ def map_by_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
714
731
  side_link_type: type[WrappedType]) -> dict[WrappedType, WrappedRecordModel]:
715
732
  """
716
733
  Take a list of record models and map them by their reverse side link. Essentially an inversion of
@@ -724,6 +741,7 @@ class RecordHandler:
724
741
  :return: A dict[SideLink, ModelType]. If an input model doesn't have a reverse side link of the given type
725
742
  pointing to it, then it will not be in the resulting dictionary.
726
743
  """
744
+ field_name: str = AliasUtil.to_data_field_name(field_name)
727
745
  to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
728
746
  .map_to_reverse_side_link(models, field_name, side_link_type)
729
747
  by_side_link: dict[WrappedType, WrappedRecordModel] = {}
@@ -750,7 +768,8 @@ class RecordHandler:
750
768
  return ret_dict
751
769
 
752
770
  @staticmethod
753
- def map_by_field(models: Iterable[SapioRecord], field_name: str) -> dict[Any, list[SapioRecord]]:
771
+ def map_by_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) \
772
+ -> dict[FieldValue, list[SapioRecord]]:
754
773
  """
755
774
  Map the given records by one of their fields. If any two records share the same field value, they'll appear in
756
775
  the same value list.
@@ -759,14 +778,16 @@ class RecordHandler:
759
778
  :param field_name: The field name to map against.
760
779
  :return: A dict mapping field values to the records with that value.
761
780
  """
762
- ret_dict: dict[Any, list[SapioRecord]] = {}
781
+ field_name: str = AliasUtil.to_data_field_name(field_name)
782
+ ret_dict: dict[FieldValue, list[SapioRecord]] = {}
763
783
  for model in models:
764
- val: Any = model.get_field_value(field_name)
784
+ val: FieldValue = model.get_field_value(field_name)
765
785
  ret_dict.setdefault(val, []).append(model)
766
786
  return ret_dict
767
787
 
768
788
  @staticmethod
769
- def map_by_unique_field(models: Iterable[SapioRecord], field_name: str) -> dict[Any, SapioRecord]:
789
+ def map_by_unique_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) \
790
+ -> dict[FieldValue, SapioRecord]:
770
791
  """
771
792
  Uniquely map the given records by one of their fields. If any two records share the same field value, throws
772
793
  an exception.
@@ -775,16 +796,17 @@ class RecordHandler:
775
796
  :param field_name: The field name to map against.
776
797
  :return: A dict mapping field values to the record with that value.
777
798
  """
778
- ret_dict: dict[Any, SapioRecord] = {}
799
+ field_name: str = AliasUtil.to_data_field_name(field_name)
800
+ ret_dict: dict[FieldValue, SapioRecord] = {}
779
801
  for model in models:
780
- val: Any = model.get_field_value(field_name)
802
+ val: FieldValue = model.get_field_value(field_name)
781
803
  if val in ret_dict:
782
804
  raise SapioException(f"Value {val} encountered more than once in models list.")
783
805
  ret_dict.update({val: model})
784
806
  return ret_dict
785
807
 
786
808
  @staticmethod
787
- def sum_of_field(models: Iterable[SapioRecord], field_name: str) -> float:
809
+ def sum_of_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) -> float:
788
810
  """
789
811
  Sum up the numeric value of a given field across all input models. Excepts that all given models have a value.
790
812
  If the field is an integer field, the value will be converted to a float.
@@ -793,13 +815,14 @@ class RecordHandler:
793
815
  :param field_name: The name of the numeric field to sum.
794
816
  :return: The sum of the field values for the collection of models.
795
817
  """
818
+ field_name: str = AliasUtil.to_data_field_name(field_name)
796
819
  field_sum: float = 0
797
820
  for model in models:
798
821
  field_sum += float(model.get_field_value(field_name))
799
822
  return field_sum
800
823
 
801
824
  @staticmethod
802
- def mean_of_field(models: Iterable[SapioRecord], field_name: str) -> float:
825
+ def mean_of_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) -> float:
803
826
  """
804
827
  Calculate the mean of the numeric value of a given field across all input models. Excepts that all given models
805
828
  have a value. If the field is an integer field, the value will be converted to a float.
@@ -840,8 +863,8 @@ class RecordHandler:
840
863
  return oldest
841
864
 
842
865
  @staticmethod
843
- def values_to_field_maps(field_name: str, values: Iterable[Any], existing_fields: list[FieldMap] | None = None) \
844
- -> list[FieldMap]:
866
+ def values_to_field_maps(field_name: FieldIdentifier, values: Iterable[FieldValue],
867
+ existing_fields: list[FieldIdentifier] | None = None) -> list[FieldMap]:
845
868
  """
846
869
  Add a list of values for a specific field to a list of dictionaries pairing each value to that field name.
847
870
 
@@ -852,6 +875,8 @@ class RecordHandler:
852
875
  :return: A fields map list that contains the given values mapped by the given field name.
853
876
  """
854
877
  # Update the existing fields map list if one is given.
878
+ field_name: str = AliasUtil.to_data_field_name(field_name)
879
+ existing_fields: list[FieldMap] = AliasUtil.to_data_field_names_list_dict(existing_fields)
855
880
  if existing_fields:
856
881
  values = list(values)
857
882
  # The number of new values must match the length of the existing fields list.
@@ -1016,8 +1041,8 @@ class RecordHandler:
1016
1041
  ret_dict.update({model: self.inst_man.wrap(current[0], wrapper_type) if current else None})
1017
1042
  return ret_dict
1018
1043
 
1019
- def __find_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: Any,
1020
- secondary_identifiers: FieldMap | None = None) -> WrappedType | None:
1044
+ def __find_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: FieldValue,
1045
+ secondary_identifiers: FieldIdentifierMap | None = None) -> WrappedType | None:
1021
1046
  """
1022
1047
  Find a record from the system that matches the given field values. The primary identifier and value is used
1023
1048
  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