sapiopycommons 2025.7.9a583__py3-none-any.whl → 2025.7.14a610__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.

@@ -3,13 +3,9 @@ from __future__ import annotations
3
3
  import io
4
4
  import warnings
5
5
  from collections.abc import Iterable
6
- from typing import Collection, TypeVar, TypeAlias
6
+ from typing import Collection
7
7
  from weakref import WeakValueDictionary
8
8
 
9
- from sapiopycommons.general.aliases import RecordModel, SapioRecord, FieldMap, FieldIdentifier, AliasUtil, \
10
- FieldIdentifierMap, FieldValue, UserIdentifier, FieldIdentifierKey, DataTypeIdentifier
11
- from sapiopycommons.general.custom_report_util import CustomReportUtil
12
- from sapiopycommons.general.exceptions import SapioException
13
9
  from sapiopylib.rest.DataRecordManagerService import DataRecordManager
14
10
  from sapiopylib.rest.User import SapioUser
15
11
  from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, RawReportTerm, ReportColumn
@@ -28,23 +24,19 @@ from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType, Wr
28
24
  from sapiopylib.rest.utils.recordmodel.RelationshipPath import RelationshipPath, RelationshipNode, \
29
25
  RelationshipNodeType
30
26
  from sapiopylib.rest.utils.recordmodel.ancestry import RecordModelAncestorManager
31
- from sapiopylib.rest.utils.recordmodel.properties import Parents, Parent, Children, Child, ForwardSideLink, \
32
- ReverseSideLink
33
-
34
- # Aliases for longer name.
35
- _PropertyGetter: TypeAlias = AbstractRecordModelPropertyGetter
36
- _PropertyAdder: TypeAlias = AbstractRecordModelPropertyAdder
37
- _PropertyRemover: TypeAlias = AbstractRecordModelPropertyRemover
38
- _PropertySetter: TypeAlias = AbstractRecordModelPropertySetter
39
- _PropertyType: TypeAlias = RecordModelPropertyType
27
+ from sapiopylib.rest.utils.recordmodel.properties import Parents, Parent, Children, Child, ForwardSideLink
40
28
 
41
- # CR-47717: Use TypeVars in the type hints of certain functions to prevent PyCharm from erroneously flagging certain
42
- # return type hints as incorrect.
43
- IsRecordModel = TypeVar('IsRecordModel', bound=RecordModel)
44
- """A PyRecordModel or AbstractRecordModel."""
45
- IsSapioRecord = TypeVar('IsSapioRecord', bound=SapioRecord)
46
- """A DataRecord, PyRecordModel, or AbstractRecordModel."""
29
+ from sapiopycommons.general.aliases import RecordModel, SapioRecord, FieldMap, FieldIdentifier, AliasUtil, \
30
+ FieldIdentifierMap, FieldValue, UserIdentifier, FieldIdentifierKey, DataTypeIdentifier
31
+ from sapiopycommons.general.custom_report_util import CustomReportUtil
32
+ from sapiopycommons.general.exceptions import SapioException
47
33
 
34
+ # Aliases for longer name.
35
+ _PropertyGetter = AbstractRecordModelPropertyGetter
36
+ _PropertyAdder = AbstractRecordModelPropertyAdder
37
+ _PropertyRemover = AbstractRecordModelPropertyRemover
38
+ _PropertySetter = AbstractRecordModelPropertySetter
39
+ _PropertyType = RecordModelPropertyType
48
40
 
49
41
  # FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
50
42
  # FR-47575 - Reordered functions so that the Java and Python versions are as close to each other as possible.
@@ -532,11 +524,9 @@ class RecordHandler:
532
524
  """
533
525
  warnings.warn("Deprecated in favor of the [System/Custom/Quick]ReportRecordAutoPager classes.", DeprecationWarning)
534
526
  if isinstance(report_name, str):
535
- # noinspection PyDeprecation
536
527
  results: list[dict[str, FieldValue]] = CustomReportUtil.run_system_report(self.user, report_name, filters,
537
528
  page_limit, page_size, page_number)
538
529
  elif isinstance(report_name, RawReportTerm):
539
- # noinspection PyDeprecation
540
530
  results: list[dict[str, FieldValue]] = CustomReportUtil.run_quick_report(self.user, report_name, filters,
541
531
  page_limit, page_size, page_number)
542
532
  elif isinstance(report_name, CustomReportCriteria):
@@ -549,7 +539,6 @@ class RecordHandler:
549
539
  # Enforce that the given custom report has a record ID column.
550
540
  if not any([x.data_type_name == dt and x.data_field_name == "RecordId" for x in report_name.column_list]):
551
541
  report_name.column_list.append(ReportColumn(dt, "RecordId", FieldType.LONG))
552
- # noinspection PyDeprecation
553
542
  results: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(self.user, report_name, filters,
554
543
  page_limit, page_size, page_number)
555
544
  else:
@@ -562,7 +551,7 @@ class RecordHandler:
562
551
  return self.query_models_by_id(wrapper_type, ids)
563
552
 
564
553
  @staticmethod
565
- def map_by_id(models: Iterable[IsSapioRecord]) -> dict[int, IsSapioRecord]:
554
+ def map_by_id(models: Iterable[SapioRecord]) -> dict[int, SapioRecord]:
566
555
  """
567
556
  Map the given records their record IDs.
568
557
 
@@ -571,12 +560,12 @@ class RecordHandler:
571
560
  """
572
561
  ret_dict: dict[int, SapioRecord] = {}
573
562
  for model in models:
574
- ret_dict.update({AliasUtil.to_record_id(model): model})
563
+ ret_dict.update({model.record_id: model})
575
564
  return ret_dict
576
565
 
577
566
  @staticmethod
578
- def map_by_field(models: Iterable[IsSapioRecord], field_name: FieldIdentifier) \
579
- -> dict[FieldValue, list[IsSapioRecord]]:
567
+ def map_by_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) \
568
+ -> dict[FieldValue, list[SapioRecord]]:
580
569
  """
581
570
  Map the given records by one of their fields. If any two records share the same field value, they'll appear in
582
571
  the same value list.
@@ -593,8 +582,8 @@ class RecordHandler:
593
582
  return ret_dict
594
583
 
595
584
  @staticmethod
596
- def map_by_unique_field(models: Iterable[IsSapioRecord], field_name: FieldIdentifier) \
597
- -> dict[FieldValue, IsSapioRecord]:
585
+ def map_by_unique_field(models: Iterable[SapioRecord], field_name: FieldIdentifier) \
586
+ -> dict[FieldValue, SapioRecord]:
598
587
  """
599
588
  Uniquely map the given records by one of their fields. If any two records share the same field value, throws
600
589
  an exception.
@@ -673,7 +662,7 @@ class RecordHandler:
673
662
  return RecordHandler.sum_of_field(models, field_name) / len(models)
674
663
 
675
664
  @staticmethod
676
- def get_newest_record(records: Iterable[IsSapioRecord]) -> IsSapioRecord:
665
+ def get_newest_record(records: Iterable[SapioRecord]) -> SapioRecord:
677
666
  """
678
667
  Get the newest record from a list of records.
679
668
 
@@ -684,7 +673,7 @@ class RecordHandler:
684
673
 
685
674
  # FR-46696: Add a function for getting the oldest record in a list, just like we have one for the newest record.
686
675
  @staticmethod
687
- def get_oldest_record(records: Iterable[IsSapioRecord]) -> IsSapioRecord:
676
+ def get_oldest_record(records: Iterable[SapioRecord]) -> SapioRecord:
688
677
  """
689
678
  Get the oldest record from a list of records.
690
679
 
@@ -694,7 +683,7 @@ class RecordHandler:
694
683
  return min(records, key=lambda x: x.record_id)
695
684
 
696
685
  @staticmethod
697
- def get_min_record(records: list[IsSapioRecord], field: FieldIdentifier) -> IsSapioRecord:
686
+ def get_min_record(records: list[RecordModel], field: FieldIdentifier) -> RecordModel:
698
687
  """
699
688
  Get the record model with the minimum value of a given field from a list of record models.
700
689
 
@@ -706,7 +695,7 @@ class RecordHandler:
706
695
  return min(records, key=lambda x: x.get_field_value(field))
707
696
 
708
697
  @staticmethod
709
- def get_max_record(records: list[IsSapioRecord], field: FieldIdentifier) -> IsSapioRecord:
698
+ def get_max_record(records: list[RecordModel], field: FieldIdentifier) -> RecordModel:
710
699
  """
711
700
  Get the record model with the maximum value of a given field from a list of record models.
712
701
 
@@ -881,7 +870,7 @@ class RecordHandler:
881
870
  parent_dt: str = AliasUtil.to_data_type_name(parent_type)
882
871
  wrapper: type[WrappedType] | None = parent_type if isinstance(parent_type, type) else None
883
872
  record: PyRecordModel = RecordModelInstanceManager.unwrap(record)
884
- parent: PyRecordModel | None = record.get(Parent.of_type_name(parent_dt))
873
+ parent: PyRecordModel | None = record.get_parent_of_type(parent_dt)
885
874
  if parent is not None:
886
875
  return self.wrap_model(parent, wrapper) if wrapper else parent
887
876
  return record.add(Parent.create(wrapper)) if wrapper else record.add(Parent.create_by_name(parent_dt))
@@ -899,7 +888,7 @@ class RecordHandler:
899
888
  child_dt: str = AliasUtil.to_data_type_name(child_type)
900
889
  wrapper: type[WrappedType] | None = child_type if isinstance(child_type, type) else None
901
890
  record: PyRecordModel = RecordModelInstanceManager.unwrap(record)
902
- child: PyRecordModel | None = record.get(Child.of_type_name(child_dt))
891
+ child: PyRecordModel | None = record.get_child_of_type(child_dt)
903
892
  if child is not None:
904
893
  return self.wrap_model(child, wrapper) if wrapper else child
905
894
  return record.add(Child.create(wrapper)) if wrapper else record.add(Child.create_by_name(child_dt))
@@ -919,7 +908,7 @@ class RecordHandler:
919
908
  side_link_field: str = AliasUtil.to_data_field_name(side_link_field)
920
909
  wrapper: type[WrappedType] | None = side_link_type if isinstance(side_link_type, type) else None
921
910
  record: PyRecordModel = RecordModelInstanceManager.unwrap(record)
922
- side_link: PyRecordModel | None = record.get(ForwardSideLink.of(side_link_field))
911
+ side_link: PyRecordModel | None = record.get_forward_side_link(side_link_field)
923
912
  if side_link is not None:
924
913
  return self.wrap_model(side_link, wrapper) if wrapper else side_link
925
914
  side_link: WrappedType | PyRecordModel = self.add_model(side_link_type)
@@ -966,63 +955,52 @@ class RecordHandler:
966
955
  if child not in children:
967
956
  record.remove(Child.ref(child))
968
957
 
969
- # CR-47717: Update the map_[to/by]_[relationship] functions to allow PyRecordModels to be provided and returned
970
- # instead of only using WrappedRecordModels and wrapper types.
971
958
  @staticmethod
972
- def map_to_parent(models: Iterable[IsRecordModel], parent_type: type[WrappedType] | str) \
973
- -> dict[IsRecordModel, WrappedType | PyRecordModel]:
959
+ def map_to_parent(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType])\
960
+ -> dict[WrappedRecordModel, WrappedType]:
974
961
  """
975
962
  Map a list of record models to a single parent of a given type. The parents must already be loaded.
976
963
 
977
964
  :param models: A list of record models.
978
- :param parent_type: The record model wrapper or data type name of the parents. If a data type name is
979
- provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
965
+ :param parent_type: The record model wrapper of the parent.
980
966
  :return: A dict[ModelType, ParentType]. If an input model doesn't have a parent of the given parent type, then
981
967
  it will map to None.
982
968
  """
983
- return_dict: dict[RecordModel, WrappedType | PyRecordModel] = {}
969
+ return_dict: dict[WrappedRecordModel, WrappedType] = {}
984
970
  for model in models:
985
- if isinstance(parent_type, str):
986
- return_dict[model] = model.get(Parent.of_type_name(parent_type))
987
- else:
988
- return_dict[model] = model.get(Parent.of_type(parent_type))
971
+ return_dict[model] = model.get_parent_of_type(parent_type)
989
972
  return return_dict
990
973
 
991
974
  @staticmethod
992
- def map_to_parents(models: Iterable[IsRecordModel], parent_type: type[WrappedType] | str) \
993
- -> dict[IsRecordModel, list[WrappedType] | list[PyRecordModel]]:
975
+ def map_to_parents(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
976
+ -> dict[WrappedRecordModel, list[WrappedType]]:
994
977
  """
995
978
  Map a list of record models to a list parents of a given type. The parents must already be loaded.
996
979
 
997
980
  :param models: A list of record models.
998
- :param parent_type: The record model wrapper or data type name of the parents. If a data type name is
999
- provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
981
+ :param parent_type: The record model wrapper of the parents.
1000
982
  :return: A dict[ModelType, list[ParentType]]. If an input model doesn't have a parent of the given parent type,
1001
983
  then it will map to an empty list.
1002
984
  """
1003
- return_dict: dict[WrappedRecordModel, list[WrappedType] | list[PyRecordModel]] = {}
985
+ return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
1004
986
  for model in models:
1005
- if isinstance(parent_type, str):
1006
- return_dict[model] = model.get(Parents.of_type_name(parent_type))
1007
- else:
1008
- return_dict[model] = model.get(Parents.of_type(parent_type))
987
+ return_dict[model] = model.get_parents_of_type(parent_type)
1009
988
  return return_dict
1010
989
 
1011
990
  @staticmethod
1012
- def map_by_parent(models: Iterable[IsRecordModel], parent_type: type[WrappedType] | str) \
1013
- -> dict[WrappedType | PyRecordModel, IsRecordModel]:
991
+ def map_by_parent(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
992
+ -> dict[WrappedType, WrappedRecordModel]:
1014
993
  """
1015
994
  Take a list of record models and map them by their parent. Essentially an inversion of map_to_parent.
1016
995
  If two records share the same parent, an exception will be thrown. The parents must already be loaded.
1017
996
 
1018
997
  :param models: A list of record models.
1019
- :param parent_type: The record model wrapper or data type name of the parents. If a data type name is
1020
- provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
998
+ :param parent_type: The record model wrapper of the parents.
1021
999
  :return: A dict[ParentType, ModelType]. If an input model doesn't have a parent of the given parent type,
1022
1000
  then it will not be in the resulting dictionary.
1023
1001
  """
1024
- to_parent: dict[RecordModel, WrappedType | PyRecordModel] = RecordHandler.map_to_parent(models, parent_type)
1025
- by_parent: dict[WrappedType | PyRecordModel, RecordModel] = {}
1002
+ to_parent: dict[WrappedRecordModel, WrappedType] = RecordHandler.map_to_parent(models, parent_type)
1003
+ by_parent: dict[WrappedType, WrappedRecordModel] = {}
1026
1004
  for record, parent in to_parent.items():
1027
1005
  if parent is None:
1028
1006
  continue
@@ -1033,81 +1011,70 @@ class RecordHandler:
1033
1011
  return by_parent
1034
1012
 
1035
1013
  @staticmethod
1036
- def map_by_parents(models: Iterable[IsRecordModel], parent_type: type[WrappedType] | str) \
1037
- -> dict[WrappedType | PyRecordModel, list[IsRecordModel]]:
1014
+ def map_by_parents(models: Iterable[WrappedRecordModel], parent_type: type[WrappedType]) \
1015
+ -> dict[WrappedType, list[WrappedRecordModel]]:
1038
1016
  """
1039
1017
  Take a list of record models and map them by their parents. Essentially an inversion of map_to_parents. Input
1040
1018
  models that share a parent will end up in the same list. The parents must already be loaded.
1041
1019
 
1042
1020
  :param models: A list of record models.
1043
- :param parent_type: The record model wrapper or data type name of the parents. If a data type name is
1044
- provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1021
+ :param parent_type: The record model wrapper of the parents.
1045
1022
  :return: A dict[ParentType, list[ModelType]]. If an input model doesn't have a parent of the given parent type,
1046
1023
  then it will not be in the resulting dictionary.
1047
1024
  """
1048
- to_parents: dict[RecordModel, list[WrappedType] | list[PyRecordModel]] = RecordHandler\
1049
- .map_to_parents(models, parent_type)
1050
- by_parents: dict[WrappedType | PyRecordModel, list[RecordModel]] = {}
1025
+ to_parents: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler.map_to_parents(models, parent_type)
1026
+ by_parents: dict[WrappedType, list[WrappedRecordModel]] = {}
1051
1027
  for record, parents in to_parents.items():
1052
1028
  for parent in parents:
1053
1029
  by_parents.setdefault(parent, []).append(record)
1054
1030
  return by_parents
1055
1031
 
1056
1032
  @staticmethod
1057
- def map_to_child(models: Iterable[IsRecordModel], child_type: type[WrappedType] | str) \
1058
- -> dict[IsRecordModel, WrappedType | PyRecordModel]:
1033
+ def map_to_child(models: Iterable[WrappedRecordModel], child_type: type[WrappedType])\
1034
+ -> dict[WrappedRecordModel, WrappedType]:
1059
1035
  """
1060
1036
  Map a list of record models to a single child of a given type. The children must already be loaded.
1061
1037
 
1062
1038
  :param models: A list of record models.
1063
- :param child_type: The record model wrapper or data type name of the children. If a data type name is
1064
- provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1039
+ :param child_type: The record model wrapper of the child.
1065
1040
  :return: A dict[ModelType, ChildType]. If an input model doesn't have a child of the given child type, then
1066
1041
  it will map to None.
1067
1042
  """
1068
- return_dict: dict[RecordModel, WrappedType | PyRecordModel] = {}
1043
+ return_dict: dict[WrappedRecordModel, WrappedType] = {}
1069
1044
  for model in models:
1070
- if isinstance(child_type, str):
1071
- return_dict[model] = model.get(Child.of_type_name(child_type))
1072
- else:
1073
- return_dict[model] = model.get(Child.of_type(child_type))
1045
+ return_dict[model] = model.get_child_of_type(child_type)
1074
1046
  return return_dict
1075
1047
 
1076
1048
  @staticmethod
1077
- def map_to_children(models: Iterable[IsRecordModel], child_type: type[WrappedType] | str) \
1078
- -> dict[IsRecordModel, list[WrappedType] | PyRecordModel]:
1049
+ def map_to_children(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
1050
+ -> dict[WrappedRecordModel, list[WrappedType]]:
1079
1051
  """
1080
1052
  Map a list of record models to a list children of a given type. The children must already be loaded.
1081
1053
 
1082
1054
  :param models: A list of record models.
1083
- :param child_type: The record model wrapper or data type name of the children. If a data type name is
1084
- provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1055
+ :param child_type: The record model wrapper of the children.
1085
1056
  :return: A dict[ModelType, list[ChildType]]. If an input model doesn't have children of the given child type,
1086
1057
  then it will map to an empty list.
1087
1058
  """
1088
- return_dict: dict[RecordModel, list[WrappedType] | list[PyRecordModel]] = {}
1059
+ return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
1089
1060
  for model in models:
1090
- if isinstance(child_type, str):
1091
- return_dict[model] = model.get(Children.of_type_name(child_type))
1092
- else:
1093
- return_dict[model] = model.get(Children.of_type(child_type))
1061
+ return_dict[model] = model.get_children_of_type(child_type)
1094
1062
  return return_dict
1095
1063
 
1096
1064
  @staticmethod
1097
- def map_by_child(models: Iterable[IsRecordModel], child_type: type[WrappedType] | str) \
1098
- -> dict[WrappedType | str, IsRecordModel]:
1065
+ def map_by_child(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
1066
+ -> dict[WrappedType, WrappedRecordModel]:
1099
1067
  """
1100
1068
  Take a list of record models and map them by their children. Essentially an inversion of map_to_child.
1101
1069
  If two records share the same child, an exception will be thrown. The children must already be loaded.
1102
1070
 
1103
1071
  :param models: A list of record models.
1104
- :param child_type: The record model wrapper or data type name of the children. If a data type name is
1105
- provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1072
+ :param child_type: The record model wrapper of the children.
1106
1073
  :return: A dict[ChildType, ModelType]. If an input model doesn't have a child of the given child type,
1107
1074
  then it will not be in the resulting dictionary.
1108
1075
  """
1109
- to_child: dict[RecordModel, WrappedType | PyRecordModel] = RecordHandler.map_to_child(models, child_type)
1110
- by_child: dict[WrappedType | PyRecordModel, RecordModel] = {}
1076
+ to_child: dict[WrappedRecordModel, WrappedType] = RecordHandler.map_to_child(models, child_type)
1077
+ by_child: dict[WrappedType, WrappedRecordModel] = {}
1111
1078
  for record, child in to_child.items():
1112
1079
  if child is None:
1113
1080
  continue
@@ -1118,50 +1085,45 @@ class RecordHandler:
1118
1085
  return by_child
1119
1086
 
1120
1087
  @staticmethod
1121
- def map_by_children(models: Iterable[IsRecordModel], child_type: type[WrappedType] | str) \
1122
- -> dict[WrappedType | PyRecordModel, list[IsRecordModel]]:
1088
+ def map_by_children(models: Iterable[WrappedRecordModel], child_type: type[WrappedType]) \
1089
+ -> dict[WrappedType, list[WrappedRecordModel]]:
1123
1090
  """
1124
1091
  Take a list of record models and map them by their children. Essentially an inversion of map_to_children. Input
1125
1092
  models that share a child will end up in the same list. The children must already be loaded.
1126
1093
 
1127
1094
  :param models: A list of record models.
1128
- :param child_type: The record model wrapper or data type name of the children. If a data type name is
1129
- provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1095
+ :param child_type: The record model wrapper of the children.
1130
1096
  :return: A dict[ChildType, list[ModelType]]. If an input model doesn't have children of the given child type,
1131
1097
  then it will not be in the resulting dictionary.
1132
1098
  """
1133
- to_children: dict[RecordModel, list[WrappedType] | list[PyRecordModel]] = RecordHandler\
1134
- .map_to_children(models, child_type)
1135
- by_children: dict[WrappedType | PyRecordModel, list[RecordModel]] = {}
1099
+ to_children: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler.map_to_children(models, child_type)
1100
+ by_children: dict[WrappedType, list[WrappedRecordModel]] = {}
1136
1101
  for record, children in to_children.items():
1137
1102
  for child in children:
1138
1103
  by_children.setdefault(child, []).append(record)
1139
1104
  return by_children
1140
1105
 
1141
1106
  @staticmethod
1142
- def map_to_forward_side_link(models: Iterable[IsRecordModel], field_name: FieldIdentifier,
1143
- side_link_type: type[WrappedType] | None) \
1144
- -> dict[IsRecordModel, WrappedType | PyRecordModel]:
1107
+ def map_to_forward_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
1108
+ side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, WrappedType]:
1145
1109
  """
1146
1110
  Map a list of record models to their forward side link. The forward side link must already be loaded.
1147
1111
 
1148
1112
  :param models: A list of record models.
1149
1113
  :param field_name: The field name on the record models where the side link is located.
1150
- :param side_link_type: The record model wrapper of the forward side link. If None, the side links will
1151
- be returned as PyRecordModels instead of WrappedRecordModels.
1114
+ :param side_link_type: The record model wrapper of the forward side link.
1152
1115
  :return: A dict[ModelType, SlideLink]. If an input model doesn't have a forward side link of the given type,
1153
1116
  then it will map to None.
1154
1117
  """
1155
1118
  field_name: str = AliasUtil.to_data_field_name(field_name)
1156
- return_dict: dict[RecordModel, WrappedType | PyRecordModel] = {}
1119
+ return_dict: dict[WrappedRecordModel, WrappedType] = {}
1157
1120
  for model in models:
1158
- return_dict[model] = model.get(ForwardSideLink.of(field_name, side_link_type))
1121
+ return_dict[model] = model.get_forward_side_link(field_name, side_link_type)
1159
1122
  return return_dict
1160
1123
 
1161
1124
  @staticmethod
1162
- def map_by_forward_side_link(models: Iterable[IsRecordModel], field_name: FieldIdentifier,
1163
- side_link_type: type[WrappedType] | None) \
1164
- -> dict[WrappedType | PyRecordModel, IsRecordModel]:
1125
+ def map_by_forward_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
1126
+ side_link_type: type[WrappedType]) -> dict[WrappedType, WrappedRecordModel]:
1165
1127
  """
1166
1128
  Take a list of record models and map them by their forward side link. Essentially an inversion of
1167
1129
  map_to_forward_side_link, but if two records share the same forward link, an exception is thrown.
@@ -1169,15 +1131,14 @@ class RecordHandler:
1169
1131
 
1170
1132
  :param models: A list of record models.
1171
1133
  :param field_name: The field name on the record models where the side link is located.
1172
- :param side_link_type: The record model wrapper of the forward side links. If None, the side links will
1173
- be returned as PyRecordModels instead of WrappedRecordModels.
1134
+ :param side_link_type: The record model wrapper of the forward side links.
1174
1135
  :return: A dict[SideLink, ModelType]. If an input model doesn't have a forward side link of the given type
1175
1136
  pointing to it, then it will not be in the resulting dictionary.
1176
1137
  """
1177
1138
  field_name: str = AliasUtil.to_data_field_name(field_name)
1178
- to_side_link: dict[RecordModel, WrappedType | PyRecordModel] = RecordHandler\
1139
+ to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
1179
1140
  .map_to_forward_side_link(models, field_name, side_link_type)
1180
- by_side_link: dict[WrappedType | PyRecordModel, RecordModel] = {}
1141
+ by_side_link: dict[WrappedType, WrappedRecordModel] = {}
1181
1142
  for record, side_link in to_side_link.items():
1182
1143
  if side_link is None:
1183
1144
  continue
@@ -1188,9 +1149,8 @@ class RecordHandler:
1188
1149
  return by_side_link
1189
1150
 
1190
1151
  @staticmethod
1191
- def map_by_forward_side_links(models: Iterable[IsRecordModel], field_name: FieldIdentifier,
1192
- side_link_type: type[WrappedType] | None) \
1193
- -> dict[WrappedType | PyRecordModel, list[IsRecordModel]]:
1152
+ def map_by_forward_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
1153
+ side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
1194
1154
  """
1195
1155
  Take a list of record models and map them by their forward side link. Essentially an inversion of
1196
1156
  map_to_forward_side_link. Input models that share a forward side link will end up in the same list.
@@ -1198,15 +1158,14 @@ class RecordHandler:
1198
1158
 
1199
1159
  :param models: A list of record models.
1200
1160
  :param field_name: The field name on the record models where the side link is located.
1201
- :param side_link_type: The record model wrapper of the forward side links. If None, the side links will
1202
- be returned as PyRecordModels instead of WrappedRecordModels.
1161
+ :param side_link_type: The record model wrapper of the forward side links.
1203
1162
  :return: A dict[SideLink, list[ModelType]]. If an input model doesn't have a forward side link of the given type
1204
1163
  pointing to it, then it will not be in the resulting dictionary.
1205
1164
  """
1206
1165
  field_name: str = AliasUtil.to_data_field_name(field_name)
1207
- to_side_link: dict[RecordModel, WrappedType | PyRecordModel] = RecordHandler\
1166
+ to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
1208
1167
  .map_to_forward_side_link(models, field_name, side_link_type)
1209
- by_side_link: dict[WrappedType | PyRecordModel, list[RecordModel]] = {}
1168
+ by_side_link: dict[WrappedType, list[WrappedRecordModel]] = {}
1210
1169
  for record, side_link in to_side_link.items():
1211
1170
  if side_link is None:
1212
1171
  continue
@@ -1214,9 +1173,8 @@ class RecordHandler:
1214
1173
  return by_side_link
1215
1174
 
1216
1175
  @staticmethod
1217
- def map_to_reverse_side_link(models: Iterable[IsRecordModel], field_name: FieldIdentifier,
1218
- side_link_type: type[WrappedType] | str) \
1219
- -> dict[IsRecordModel, WrappedType | PyRecordModel]:
1176
+ def map_to_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
1177
+ side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, WrappedType]:
1220
1178
  """
1221
1179
  Map a list of record models to the reverse side link of a given type. If a given record has more than one
1222
1180
  reverse side link of this type, an exception is thrown. The reverse side links must already be loaded.
@@ -1224,18 +1182,14 @@ class RecordHandler:
1224
1182
  :param models: A list of record models.
1225
1183
  :param field_name: The field name on the side linked model where the side link to the given record models is
1226
1184
  located.
1227
- :param side_link_type: The record model wrapper or data type name of the reverse side links. If a data type
1228
- name is provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1185
+ :param side_link_type: The record model wrapper of the reverse side links.
1229
1186
  :return: A dict[ModelType, SideLink]. If an input model doesn't have reverse side links of the given type,
1230
1187
  then it will map to None.
1231
1188
  """
1232
1189
  field_name: str = AliasUtil.to_data_field_name(field_name)
1233
- return_dict: dict[RecordModel, WrappedType | PyRecordModel] = {}
1190
+ return_dict: dict[WrappedRecordModel, WrappedType] = {}
1234
1191
  for model in models:
1235
- if isinstance(side_link_type, str):
1236
- links: list[WrappedType] = model.get(ReverseSideLink.of(side_link_type, field_name))
1237
- else:
1238
- links: list[WrappedType] = model.get(ReverseSideLink.of_type(side_link_type, field_name))
1192
+ links: list[WrappedType] = model.get_reverse_side_link(field_name, side_link_type)
1239
1193
  if len(links) > 1:
1240
1194
  raise SapioException(f"Model {model.data_type_name} {model.record_id} has more than one reverse link "
1241
1195
  f"of type {side_link_type.get_wrapper_data_type_name()}.")
@@ -1243,9 +1197,8 @@ class RecordHandler:
1243
1197
  return return_dict
1244
1198
 
1245
1199
  @staticmethod
1246
- def map_to_reverse_side_links(models: Iterable[IsRecordModel], field_name: FieldIdentifier,
1247
- side_link_type: type[WrappedType] | str) \
1248
- -> dict[IsRecordModel, list[WrappedType] | list[PyRecordModel]]:
1200
+ def map_to_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
1201
+ side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, list[WrappedType]]:
1249
1202
  """
1250
1203
  Map a list of record models to a list reverse side links of a given type. The reverse side links must already
1251
1204
  be loaded.
@@ -1253,24 +1206,19 @@ class RecordHandler:
1253
1206
  :param models: A list of record models.
1254
1207
  :param field_name: The field name on the side linked model where the side link to the given record models is
1255
1208
  located.
1256
- :param side_link_type: The record model wrapper or data type name of the reverse side links. If a data type
1257
- name is provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1209
+ :param side_link_type: The record model wrapper of the reverse side links.
1258
1210
  :return: A dict[ModelType, list[SideLink]]. If an input model doesn't have reverse side links of the given type,
1259
1211
  then it will map to an empty list.
1260
1212
  """
1261
1213
  field_name: str = AliasUtil.to_data_field_name(field_name)
1262
- return_dict: dict[RecordModel, list[WrappedType] | list[PyRecordModel]] = {}
1214
+ return_dict: dict[WrappedRecordModel, list[WrappedType]] = {}
1263
1215
  for model in models:
1264
- if isinstance(side_link_type, str):
1265
- return_dict[model] = model.get(ReverseSideLink.of(side_link_type, field_name))
1266
- else:
1267
- return_dict[model] = model.get(ReverseSideLink.of_type(side_link_type, field_name))
1216
+ return_dict[model] = model.get_reverse_side_link(field_name, side_link_type)
1268
1217
  return return_dict
1269
1218
 
1270
1219
  @staticmethod
1271
- def map_by_reverse_side_link(models: Iterable[IsRecordModel], field_name: FieldIdentifier,
1272
- side_link_type: type[WrappedType] | str) \
1273
- -> dict[WrappedType | PyRecordModel, IsRecordModel]:
1220
+ def map_by_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
1221
+ side_link_type: type[WrappedType]) -> dict[WrappedType, WrappedRecordModel]:
1274
1222
  """
1275
1223
  Take a list of record models and map them by their reverse side link. Essentially an inversion of
1276
1224
  map_to_reverse_side_link. If two records share the same reverse side link, an exception is thrown.
@@ -1279,15 +1227,14 @@ class RecordHandler:
1279
1227
  :param models: A list of record models.
1280
1228
  :param field_name: The field name on the side linked model where the side link to the given record models is
1281
1229
  located.
1282
- :param side_link_type: The record model wrapper or data type name of the reverse side links. If a data type
1283
- name is provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1230
+ :param side_link_type: The record model wrapper of the reverse side links.
1284
1231
  :return: A dict[SideLink, ModelType]. If an input model doesn't have a reverse side link of the given type
1285
1232
  pointing to it, then it will not be in the resulting dictionary.
1286
1233
  """
1287
1234
  field_name: str = AliasUtil.to_data_field_name(field_name)
1288
- to_side_link: dict[RecordModel, WrappedType | PyRecordModel] = RecordHandler\
1235
+ to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
1289
1236
  .map_to_reverse_side_link(models, field_name, side_link_type)
1290
- by_side_link: dict[WrappedType | PyRecordModel, RecordModel] = {}
1237
+ by_side_link: dict[WrappedType, WrappedRecordModel] = {}
1291
1238
  for record, side_link in to_side_link.items():
1292
1239
  if side_link is None:
1293
1240
  continue
@@ -1298,8 +1245,8 @@ class RecordHandler:
1298
1245
  return by_side_link
1299
1246
 
1300
1247
  @staticmethod
1301
- def map_by_reverse_side_links(models: Iterable[IsRecordModel], field_name: FieldIdentifier,
1302
- side_link_type: type[WrappedType] | str) -> dict[WrappedType | PyRecordModel, list[IsRecordModel]]:
1248
+ def map_by_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: FieldIdentifier,
1249
+ side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
1303
1250
  """
1304
1251
  Take a list of record models and map them by their reverse side links. Essentially an inversion of
1305
1252
  map_to_reverse_side_links. Input models that share a reverse side link will end up in the same list.
@@ -1308,8 +1255,7 @@ class RecordHandler:
1308
1255
  :param models: A list of record models.
1309
1256
  :param field_name: The field name on the side linked model where the side link to the given record models is
1310
1257
  located.
1311
- :param side_link_type: The record model wrapper or data type name of the reverse side links. If a data type
1312
- name is provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
1258
+ :param side_link_type: The record model wrapper of the reverse side links.
1313
1259
  :return: A dict[SideLink, list[ModelType]]. If an input model doesn't have reverse side links of the given type
1314
1260
  pointing to it, then it will not be in the resulting dictionary.
1315
1261
  """
@@ -1324,9 +1270,9 @@ class RecordHandler:
1324
1270
 
1325
1271
  # FR-46155: Update relationship path traversing functions to be non-static and take in a wrapper type so that the
1326
1272
  # output can be wrapped instead of requiring the user to wrap the output.
1327
- def get_linear_path(self, models: Iterable[IsRecordModel], path: RelationshipPath,
1273
+ def get_linear_path(self, models: Iterable[RecordModel], path: RelationshipPath,
1328
1274
  wrapper_type: type[WrappedType] | None = None) \
1329
- -> dict[IsRecordModel, WrappedType | PyRecordModel | None]:
1275
+ -> dict[RecordModel, WrappedType | PyRecordModel | None]:
1330
1276
  """
1331
1277
  Given a relationship path, travel the path starting from the input models. Returns the record at the end of the
1332
1278
  path, if any. The hierarchy must be linear (1:1 relationship between data types at every step) and the
@@ -1339,7 +1285,7 @@ class RecordHandler:
1339
1285
  :return: Each record model mapped to the record at the end of the path starting from itself. If the end of the
1340
1286
  path couldn't be reached, the record will map to None.
1341
1287
  """
1342
- ret_dict: dict[RecordModel, WrappedType | PyRecordModel | None] = {}
1288
+ ret_dict: dict[RecordModel, WrappedType | None] = {}
1343
1289
  # PR-46832: Update path traversal to account for changes to RelationshipPath in Sapiopylib.
1344
1290
  path: list[RelationshipNode] = path.path
1345
1291
  for model in models:
@@ -1386,9 +1332,9 @@ class RecordHandler:
1386
1332
  ret_dict.update({model: self.wrap_model(current, wrapper_type) if current else None})
1387
1333
  return ret_dict
1388
1334
 
1389
- def get_branching_path(self, models: Iterable[IsRecordModel], path: RelationshipPath,
1335
+ def get_branching_path(self, models: Iterable[RecordModel], path: RelationshipPath,
1390
1336
  wrapper_type: type[WrappedType] | None = None)\
1391
- -> dict[IsRecordModel, list[WrappedType] | list[PyRecordModel]]:
1337
+ -> dict[RecordModel, list[WrappedType] | list[PyRecordModel]]:
1392
1338
  """
1393
1339
  Given a relationship path, travel the path starting from the input models. Returns the record at the end of the
1394
1340
  path, if any. The hierarchy may be non-linear (1:Many relationships between data types are allowed) and the
@@ -1401,7 +1347,7 @@ class RecordHandler:
1401
1347
  :return: Each record model mapped to the records at the end of the path starting from itself. If the end of the
1402
1348
  path couldn't be reached, the record will map to an empty list.
1403
1349
  """
1404
- ret_dict: dict[RecordModel, list[WrappedType] | list[PyRecordModel]] = {}
1350
+ ret_dict: dict[RecordModel, list[WrappedType]] = {}
1405
1351
  # PR-46832: Update path traversal to account for changes to RelationshipPath in Sapiopylib.
1406
1352
  path: list[RelationshipNode] = path.path
1407
1353
  for model in models:
@@ -1437,9 +1383,9 @@ class RecordHandler:
1437
1383
 
1438
1384
  # FR-46155: Create a relationship traversing function that returns a single function at the end of the path like
1439
1385
  # get_linear_path but can handle branching paths in the middle of the search like get_branching_path.
1440
- def get_flat_path(self, models: Iterable[IsRecordModel], path: RelationshipPath,
1386
+ def get_flat_path(self, models: Iterable[RecordModel], path: RelationshipPath,
1441
1387
  wrapper_type: type[WrappedType] | None = None) \
1442
- -> dict[IsRecordModel, WrappedType | PyRecordModel | None]:
1388
+ -> dict[RecordModel, WrappedType | PyRecordModel | None]:
1443
1389
  """
1444
1390
  Given a relationship path, travel the path starting from the input models. Returns the record at the end of the
1445
1391
  path, if any. The hierarchy may be non-linear (1:Many relationships between data types are allowed) and the
@@ -1456,7 +1402,7 @@ class RecordHandler:
1456
1402
  :return: Each record model mapped to the record at the end of the path starting from itself. If the end of the
1457
1403
  path couldn't be reached, the record will map to None.
1458
1404
  """
1459
- ret_dict: dict[RecordModel, WrappedType | PyRecordModel | None] = {}
1405
+ ret_dict: dict[RecordModel, WrappedType | None] = {}
1460
1406
  # PR-46832: Update path traversal to account for changes to RelationshipPath in Sapiopylib.
1461
1407
  path: list[RelationshipNode] = path.path
1462
1408
  for model in models: