sapiopycommons 2025.2.25a450__py3-none-any.whl → 2025.3.6a453__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sapiopycommons might be problematic. Click here for more details.

@@ -1,7 +1,6 @@
1
1
  import base64
2
2
  import io
3
3
  import math
4
- import re
5
4
  from typing import Final, Mapping, Any
6
5
 
7
6
  import requests
@@ -26,7 +25,7 @@ from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment
26
25
  from sapiopylib.rest.pojo.eln.ExperimentEntry import ExperimentEntry
27
26
  from sapiopylib.rest.pojo.eln.ExperimentEntryCriteria import ElnEntryCriteria, ElnFormEntryUpdateCriteria, \
28
27
  ElnDashboardEntryUpdateCriteria
29
- from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnEntryType, ElnBaseDataType
28
+ from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnEntryType, ElnBaseDataType, ExperimentEntryStatus
30
29
  from sapiopylib.rest.pojo.eln.eln_headings import ElnExperimentTabAddCriteria, ElnExperimentTab
31
30
  from sapiopylib.rest.pojo.eln.field_set import ElnFieldSetInfo
32
31
  from sapiopylib.rest.utils.ProtocolUtils import ELNStepFactory
@@ -35,6 +34,7 @@ from sapiopylib.rest.utils.Protocols import ElnEntryStep, ElnExperimentProtocol
35
34
  from sapiopycommons.callbacks.field_builder import FieldBuilder
36
35
  from sapiopycommons.general.aliases import AliasUtil, SapioRecord
37
36
  from sapiopycommons.general.exceptions import SapioException
37
+ from sapiopycommons.general.html_formatter import HtmlFormatter
38
38
  from sapiopycommons.general.time_util import TimeUtil
39
39
  from sapiopycommons.multimodal.multimodal import MultiModalManager
40
40
  from sapiopycommons.multimodal.multimodal_data import ImageDataRequestPojo
@@ -102,88 +102,6 @@ def format_tot_headers(headers: Mapping[str, str]) -> dict[str, str]:
102
102
  return {k.lower(): v for k, v in headers.items()}
103
103
 
104
104
 
105
- class HtmlFormatter:
106
- """
107
- A class for formatting text in HTML with tag classes supported by the client.
108
- """
109
- TIMESTAMP_TEXT__CSS_CLASS_NAME: Final[str] = "timestamp-text"
110
- HEADER_1_TEXT__CSS_CLASS_NAME: Final[str] = "header1-text"
111
- HEADER_2_TEXT__CSS_CLASS_NAME: Final[str] = "header2-text"
112
- HEADER_3_TEXT__CSS_CLASS_NAME: Final[str] = "header3-text"
113
- BODY_TEXT__CSS_CLASS_NAME: Final[str] = "body-text"
114
- CAPTION_TEXT__CSS_CLASS_NAME: Final[str] = "caption-text"
115
-
116
- @staticmethod
117
- def timestamp(text: str) -> str:
118
- """
119
- Given a text string, return that same text string HTML formatted using the timestamp CSS class.
120
-
121
- :param text: The text to format.
122
- :return: The HTML formatted text.
123
- """
124
- return f"<span class=\"{HtmlFormatter.TIMESTAMP_TEXT__CSS_CLASS_NAME}\">{text}</span>"
125
-
126
- @staticmethod
127
- def header_1(text: str) -> str:
128
- """
129
- Given a text string, return that same text string HTML formatted using the header 1 CSS class.
130
-
131
- :param text: The text to format.
132
- :return: The HTML formatted text.
133
- """
134
- return f"<span class=\"{HtmlFormatter.HEADER_1_TEXT__CSS_CLASS_NAME}\">{text}</span>"
135
-
136
- @staticmethod
137
- def header_2(text: str) -> str:
138
- """
139
- Given a text string, return that same text string HTML formatted using the header 2 CSS class.
140
-
141
- :param text: The text to format.
142
- :return: The HTML formatted text.
143
- """
144
- return f"<span class=\"{HtmlFormatter.HEADER_2_TEXT__CSS_CLASS_NAME}\">{text}</span>"
145
-
146
- @staticmethod
147
- def header_3(text: str) -> str:
148
- """
149
- Given a text string, return that same text string HTML formatted using the header 3 CSS class.
150
-
151
- :param text: The text to format.
152
- :return: The HTML formatted text.
153
- """
154
- return f"<span class=\"{HtmlFormatter.HEADER_3_TEXT__CSS_CLASS_NAME}\">{text}</span>"
155
-
156
- @staticmethod
157
- def body(text: str) -> str:
158
- """
159
- Given a text string, return that same text string HTML formatted using the body CSS class.
160
-
161
- :param text: The text to format.
162
- :return: The HTML formatted text.
163
- """
164
- return f"<span class=\"{HtmlFormatter.BODY_TEXT__CSS_CLASS_NAME}\">{text}</span>"
165
-
166
- @staticmethod
167
- def caption(text: str) -> str:
168
- """
169
- Given a text string, return that same text string HTML formatted using the caption CSS class.
170
-
171
- :param text: The text to format.
172
- :return: The HTML formatted text.
173
- """
174
- return f"<span class=\"{HtmlFormatter.CAPTION_TEXT__CSS_CLASS_NAME}\">{text}</span>"
175
-
176
- @staticmethod
177
- def replace_newlines(text: str) -> str:
178
- """
179
- Given a text string, return that same text string HTML formatted with newlines replaced by HTML line breaks.
180
-
181
- :param text: The text to format.
182
- :return: The HTML formatted text.
183
- """
184
- return re.sub("\r?\n", "<br>", text)
185
-
186
-
187
105
  class AiHelper:
188
106
  """
189
107
  A class with helper methods for the AI to make use of when creating/updating experiment tabs and entries.
@@ -611,6 +529,8 @@ class ToolOfToolsHelper:
611
529
  # Stuff created by this helper.
612
530
  _initialized: bool
613
531
  """Whether a tab for this tool has been initialized."""
532
+ _new_tab: bool
533
+ """Whether a new tab was created for this tool."""
614
534
  tab: ElnExperimentTab
615
535
  """The tab that contains the tool's entries."""
616
536
  description_entry: ElnEntryStep | None
@@ -649,6 +569,15 @@ class ToolOfToolsHelper:
649
569
  self.eln_man = ElnManager(self.user)
650
570
 
651
571
  self._initialized = False
572
+ self._new_tab = False
573
+
574
+ @property
575
+ def is_initialized(self) -> bool:
576
+ return self._initialized
577
+
578
+ @property
579
+ def is_new_tab(self) -> bool:
580
+ return self._new_tab
652
581
 
653
582
  def initialize_tab(self) -> ElnExperimentTab:
654
583
  if self._initialized:
@@ -701,6 +630,7 @@ class ToolOfToolsHelper:
701
630
 
702
631
  # Otherwise, create the tab for the tool progress and results.
703
632
  self.tab = self.helper.create_tab(tab_name)
633
+ self._new_tab = True
704
634
 
705
635
  # Create a hidden entry for tracking the progress of the tool.
706
636
  field_sets: list[ElnFieldSetInfo] = self.eln_man.get_field_set_info_list()
@@ -708,16 +638,18 @@ class ToolOfToolsHelper:
708
638
  x.field_set_name == "Tool of Tools Progress"]
709
639
  if not progress_field_set:
710
640
  raise SapioException("Unable to locate the field set for the Tool of Tools progress.")
711
- progress_entry_crit = ElnEntryCriteria(ElnEntryType.Form, f"{tab_name} Progress",
712
- ElnBaseDataType.EXPERIMENT_DETAIL.data_type_name, 1,
713
- notebook_experiment_tab_id=self.tab.tab_id,
714
- enb_field_set_id=progress_field_set[0].field_set_id)
641
+ progress_entry_crit = _ElnEntryCriteria(ElnEntryType.Form, f"{tab_name} Progress",
642
+ ElnBaseDataType.EXPERIMENT_DETAIL.data_type_name, 1,
643
+ notebook_experiment_tab_id=self.tab.tab_id,
644
+ enb_field_set_id=progress_field_set[0].field_set_id,
645
+ is_hidden=True)
715
646
  progress_entry = ElnEntryStep(self.helper.protocol,
716
647
  self.eln_man.add_experiment_entry(self.exp_id, progress_entry_crit))
717
648
  self.progress_entry = progress_entry
718
649
  self.progress_record = progress_entry.get_records()[0]
719
650
 
720
651
  # Hide the progress entry.
652
+ # TODO: Remove once we get this working on entry creation.
721
653
  form_update_crit = ElnFormEntryUpdateCriteria()
722
654
  form_update_crit.is_hidden = True
723
655
  self.eln_man.update_experiment_entry(self.exp_id, self.progress_entry.get_id(), form_update_crit)
@@ -735,10 +667,11 @@ class ToolOfToolsHelper:
735
667
  # Create a gauge entry to display the progress.
736
668
  gauge_entry: ElnEntryStep = _ELNStepFactory._create_gauge_chart(self.helper.protocol, progress_entry,
737
669
  f"{self.name} Progress", "Progress", "StatusMsg",
738
- column_order=2, column_span=2)
670
+ column_order=2, column_span=2, entry_height=250)
739
671
  self.progress_gauge_entry = gauge_entry
740
672
 
741
673
  # Make sure the gauge entry isn't too big and stick it to the right of the text entry.
674
+ # TODO: Remove once we get this working on entry creation.
742
675
  dash_update_crit = ElnDashboardEntryUpdateCriteria()
743
676
  dash_update_crit.entry_height = 250
744
677
  self.eln_man.update_experiment_entry(self.exp_id, self.progress_gauge_entry.get_id(), dash_update_crit)
@@ -927,8 +860,8 @@ class _ELNStepFactory:
927
860
  if order is None:
928
861
  order = last_step.eln_entry.order + 1
929
862
  eln_manager = DataMgmtServer.get_eln_manager(protocol.user)
930
- entry_criteria = ElnEntryCriteria(entry_type, step_name, data_type_name=data_type_name,
931
- order=order, notebook_experiment_tab_id=tab_id, **kwargs)
863
+ entry_criteria = _ElnEntryCriteria(entry_type, step_name, data_type_name, order,
864
+ notebook_experiment_tab_id=tab_id, **kwargs)
932
865
  new_entry: ExperimentEntry = eln_manager.add_experiment_entry(protocol.eln_experiment.notebook_experiment_id,
933
866
  entry_criteria)
934
867
  return eln_manager, new_entry
@@ -945,3 +878,40 @@ class _GaugeChartDefinition(GaugeChartDefinition):
945
878
  "dataFieldName": self.status_field
946
879
  }
947
880
  return result
881
+
882
+
883
+ class _ElnEntryCriteria(ElnEntryCriteria):
884
+ is_hidden: bool | None
885
+ entry_height: int | None
886
+ description: str | None
887
+ is_initialization_required: bool | None
888
+ collapse_entry: bool | None
889
+ entry_status: ExperimentEntryStatus | None
890
+ template_item_fulfilled_timestamp: int | None
891
+
892
+ def __init__(self, entry_type: ElnEntryType, entry_name: str | None, data_type_name: str | None, order: int,
893
+ is_hidden: bool | None = None, entry_height: int | None = None, description: str | None = None,
894
+ is_initialization_required: bool | None = None, collapse_entry: bool | None = None,
895
+ entry_status: ExperimentEntryStatus | None = None, template_item_fulfilled_timestamp: int | None = None,
896
+ **kwargs):
897
+ super().__init__(entry_type, entry_name, data_type_name, order, **kwargs)
898
+ self.is_hidden = is_hidden
899
+ self.entry_height = entry_height
900
+ self.description = description
901
+ self.is_initialization_required = is_initialization_required
902
+ self.collapse_entry = collapse_entry
903
+ self.entry_status = entry_status
904
+ self.template_item_fulfilled_timestamp = template_item_fulfilled_timestamp
905
+
906
+ def to_json(self) -> dict[str, Any]:
907
+ ret: dict[str, Any] = super().to_json()
908
+ ret.update({
909
+ "hidden": self.is_hidden,
910
+ "entryHeight": self.entry_height,
911
+ "description": self.description,
912
+ "initializationRequired": self.is_initialization_required,
913
+ "collapsed": self.collapse_entry,
914
+ "entryStatus": self.entry_status,
915
+ "templateItemFulfilledTimestamp": self.template_item_fulfilled_timestamp
916
+ })
917
+ return ret
@@ -712,7 +712,7 @@ class CallbackUtil:
712
712
  if not records:
713
713
  raise SapioException("No records provided.")
714
714
  data_type: str = AliasUtil.to_singular_data_type_name(records)
715
- field_map_list: list[FieldMap] = AliasUtil.to_field_map_lists(records)
715
+ field_map_list: list[FieldMap] = AliasUtil.to_field_map_list(records)
716
716
 
717
717
  # Convert the group_by parameter to a field name.
718
718
  if group_by is not None:
@@ -1192,7 +1192,7 @@ class CallbackUtil:
1192
1192
  record: SapioRecord | FieldMap | None = row_records.get(field.data_type_name)
1193
1193
  # This could be either a record, a field map, or null. Convert any records to field maps.
1194
1194
  if not isinstance(record, dict) and record is not None:
1195
- record: FieldMap | None = AliasUtil.to_field_map_lists([record])[0]
1195
+ record: FieldMap | None = AliasUtil.to_field_map(record)
1196
1196
 
1197
1197
  # Find out if this field had its data type prepended to it. If this is the case, then we need to find
1198
1198
  # the true data field name before retrieving the value from the field map.
@@ -1383,7 +1383,7 @@ class CallbackUtil:
1383
1383
  if not records:
1384
1384
  raise SapioException("No records provided.")
1385
1385
  data_type: str = AliasUtil.to_singular_data_type_name(records)
1386
- field_map_list: list[FieldMap] = AliasUtil.to_field_map_lists(records)
1386
+ field_map_list: list[FieldMap] = AliasUtil.to_field_map_list(records, include_record_id=True)
1387
1387
 
1388
1388
  # Key fields display their columns in order before all non-key fields.
1389
1389
  # Unmark key fields so that the column order is respected exactly as the caller provides it.
@@ -8,6 +8,7 @@ from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, CustomReport
8
8
  from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
9
9
  from sapiopylib.rest.utils.autopaging import SapioPyAutoPager, PagerResultCriteriaType, _default_report_page_size, \
10
10
  _default_record_page_size
11
+ from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
11
12
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
12
13
 
13
14
  from sapiopycommons.general.aliases import FieldValue, UserIdentifier, AliasUtil, RecordModel
@@ -110,20 +111,20 @@ class QuickReportDictAutoPager(_DictReportPagerBase):
110
111
  super().__init__(user, first_page_criteria)
111
112
 
112
113
 
113
- class _RecordReportPagerBase(SapioPyAutoPager[CustomReportCriteria, WrappedType], ABC):
114
+ class _RecordReportPagerBase(SapioPyAutoPager[CustomReportCriteria, RecordModel], ABC):
114
115
  """
115
116
  A base class for automatically paging through a report and returning the results as a list of records.
116
117
  """
117
118
  _columns: list[ReportColumn]
118
- _wrapper: type[WrappedType]
119
+ _query_type: type[WrappedType] | str
119
120
  _data_type: str
120
121
  _rec_handler: RecordHandler
121
122
  _report_man: CustomReportManager
122
123
 
123
- def __init__(self, user: UserIdentifier, first_page_criteria: CustomReportCriteria, wrapper_type: type[WrappedType]):
124
+ def __init__(self, user: UserIdentifier, first_page_criteria: CustomReportCriteria, wrapper_type: type[WrappedType] | str):
124
125
  self._columns = first_page_criteria.column_list
125
- self._wrapper = wrapper_type
126
- self._data_type = wrapper_type.get_wrapper_data_type_name()
126
+ self._query_type = wrapper_type
127
+ self._data_type = AliasUtil.to_data_type_name(wrapper_type)
127
128
  self._rec_handler = RecordHandler(user)
128
129
  super().__init__(AliasUtil.to_sapio_user(user), first_page_criteria)
129
130
  self._report_man = DataMgmtServer.get_custom_report_manager(self.user)
@@ -139,9 +140,9 @@ class _RecordReportPagerBase(SapioPyAutoPager[CustomReportCriteria, WrappedType]
139
140
  def default_first_page_criteria(self) -> PagerResultCriteriaType:
140
141
  raise ValueError("Cannot generate a default first page criteria for custom reports.")
141
142
 
142
- def get_next_page_result(self) -> tuple[CustomReportCriteria | None, Queue[WrappedType]]:
143
+ def get_next_page_result(self) -> tuple[CustomReportCriteria | None, Queue[WrappedType] | Queue[PyRecordModel]]:
143
144
  report: CustomReport = self._report_man.run_custom_report(self.next_page_criteria)
144
- queue: Queue[WrappedType] = Queue()
145
+ queue = Queue()
145
146
  id_index: int = -1
146
147
  for i, column in enumerate(self._columns):
147
148
  if column.data_type_name == self._data_type and column.data_field_name == "RecordId":
@@ -151,7 +152,7 @@ class _RecordReportPagerBase(SapioPyAutoPager[CustomReportCriteria, WrappedType]
151
152
  raise SapioException(f"This report does not contain a Record ID column for the given record model type "
152
153
  f"{self._data_type}.")
153
154
  ids: list[int] = [row[id_index] for row in report.result_table]
154
- for row in self._rec_handler.query_models_by_id(self._wrapper, ids, page_size=report.page_size):
155
+ for row in self._rec_handler.query_models_by_id(self._query_type, ids, page_size=report.page_size):
155
156
  queue.put(row)
156
157
  if report.has_next_page:
157
158
  next_page_criteria = copy(self.next_page_criteria)
@@ -165,12 +166,15 @@ class CustomReportRecordAutoPager(_RecordReportPagerBase):
165
166
  """
166
167
  A class that automatically pages through a custom report and returns the results as a list of records.
167
168
  """
168
- def __init__(self, user: UserIdentifier, report_criteria: CustomReportCriteria, wrapper_type: type[WrappedType],
169
- page_number: int = 0, page_size: int = _default_record_page_size):
169
+ def __init__(self, user: UserIdentifier, report_criteria: CustomReportCriteria,
170
+ wrapper_type: type[WrappedType] | str, page_number: int = 0,
171
+ page_size: int = _default_record_page_size):
170
172
  """
171
173
  :param user: The current webhook context or a user object to send requests from.
172
174
  :param report_criteria: The custom report criteria to run.
173
- :param wrapper_type: The record model wrapper type to use for the records.
175
+ :param wrapper_type: The record model wrapper type or data type name of the records being searched for.
176
+ If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
177
+ instead of WrappedRecordModels.
174
178
  :param page_number: The page number to start on. The first page is page 0.
175
179
  :param page_size: The number of results to return per page.
176
180
  """
@@ -188,12 +192,14 @@ class SystemReportRecordAutoPager(_RecordReportPagerBase):
188
192
  System reports are also known as predefined searches in the system and must be defined in the data designer for
189
193
  a specific data type. That is, saved searches created by users cannot be run using this function.
190
194
  """
191
- def __init__(self, user: UserIdentifier, report_name: str, wrapper_type: type[WrappedType],
195
+ def __init__(self, user: UserIdentifier, report_name: str, wrapper_type: type[WrappedType] | str,
192
196
  page_number: int = 0, page_size: int = _default_record_page_size):
193
197
  """
194
198
  :param user: The current webhook context or a user object to send requests from.
195
199
  :param report_name: The name of the system report to run.
196
- :param wrapper_type: The record model wrapper type to use for the records.
200
+ :param wrapper_type: The record model wrapper type or data type name of the records being searched for.
201
+ If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
202
+ instead of WrappedRecordModels.
197
203
  :param page_number: The page number to start on. The first page is page 0.
198
204
  :param page_size: The number of results to return per page.
199
205
  """
@@ -208,12 +214,14 @@ class QuickReportRecordAutoPager(_RecordReportPagerBase):
208
214
  """
209
215
  A class that automatically pages through a quick report and returns the results as a list of records.
210
216
  """
211
- def __init__(self, user: UserIdentifier, report_term: RawReportTerm, wrapper_type: type[WrappedType],
217
+ def __init__(self, user: UserIdentifier, report_term: RawReportTerm, wrapper_type: type[WrappedType] | str,
212
218
  page_number: int = 0, page_size: int = _default_record_page_size):
213
219
  """
214
220
  :param user: The current webhook context or a user object to send requests from.
215
221
  :param report_term: The raw report term to use for the quick report.
216
- :param wrapper_type: The record model wrapper type to use for the records.
222
+ :param wrapper_type: The record model wrapper type or data type name of the records being searched for.
223
+ If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
224
+ instead of WrappedRecordModels.
217
225
  :param page_number: The page number to start on. The first page is page 0.
218
226
  :param page_size: The number of results to return per page.
219
227
  """
@@ -225,12 +233,12 @@ class QuickReportRecordAutoPager(_RecordReportPagerBase):
225
233
  super().__init__(user, first_page_criteria, wrapper_type)
226
234
 
227
235
 
228
- def _add_record_id_column(report: CustomReportCriteria, wrapper_type: type[WrappedType]) -> None:
236
+ def _add_record_id_column(report: CustomReportCriteria, wrapper_type: type[WrappedType] | str) -> None:
229
237
  """
230
238
  Given a custom report criteria, ensure that the report contains a Record ID column for the given record model's
231
239
  data type. Add one if it is missing.
232
240
  """
233
- dt: str = wrapper_type.get_wrapper_data_type_name()
241
+ dt: str = AliasUtil.to_data_type_name(wrapper_type)
234
242
  # Ensure that the root data type is the one we're looking for.
235
243
  report.root_data_type = dt
236
244
  # Enforce that the given custom report has a record ID column.
@@ -307,7 +307,7 @@ class FieldColumn(ColumnDef):
307
307
  elif self.search_order == FieldSearchOrder.BUNDLE_ONLY:
308
308
  return row.fields.get(self.field_name)
309
309
  elif self.search_order == FieldSearchOrder.RECORD_FIRST:
310
- fields: dict[str, Any] = AliasUtil.to_field_map_lists([record])[0] if record else {}
310
+ fields: dict[str, Any] = AliasUtil.to_field_map(record) if record else {}
311
311
  if self.field_name not in fields or (self.skip_none_values and fields.get(self.field_name) is None):
312
312
  return row.fields.get(self.field_name)
313
313
  return fields.get(self.field_name)
@@ -207,10 +207,12 @@ class AliasUtil:
207
207
  f"field with the name \"{field}\",")
208
208
 
209
209
  @staticmethod
210
- def to_field_map(record: SapioRecord) -> FieldMap:
210
+ def to_field_map(record: SapioRecord, include_record_id: bool = False) -> FieldMap:
211
211
  """
212
- Convert a given record value to a field map. This includes the given RecordId of the given record.
212
+ Convert a given record value to a field map.
213
213
 
214
+ :param record: A record which is a DataRecord, PyRecordModel, or WrappedRecordModel.
215
+ :param include_record_id: If true, include the record ID of the record in the field map using the RecordId key.
214
216
  :return: The field map for the input record.
215
217
  """
216
218
  if isinstance(record, DataRecord):
@@ -218,20 +220,25 @@ class AliasUtil:
218
220
  fields: FieldMap = record.get_fields()
219
221
  else:
220
222
  fields: FieldMap = record.fields.copy_to_dict()
221
- fields["RecordId"] = AliasUtil.to_record_id(record)
223
+ # PR-47457: Only include the record ID if the caller requests it, since including the record ID can break
224
+ # callbacks in certain circumstances if the record ID is negative.
225
+ if include_record_id:
226
+ fields["RecordId"] = AliasUtil.to_record_id(record)
222
227
  return fields
223
228
 
224
229
  @staticmethod
225
- def to_field_map_lists(records: Iterable[SapioRecord]) -> list[FieldMap]:
230
+ def to_field_map_list(records: Iterable[SapioRecord], include_record_id: bool = False) -> list[FieldMap]:
226
231
  """
227
232
  Convert a list of variables that could either be DataRecords, PyRecordModels, or WrappedRecordModels
228
233
  to a list of their field maps. This includes the given RecordId of the given records.
229
234
 
235
+ :param records: An iterable of records which are DataRecords, PyRecordModels, or WrappedRecordModels.
236
+ :param include_record_id: If true, include the record ID of the records in the field map using the RecordId key.
230
237
  :return: A list of field maps for the input records.
231
238
  """
232
239
  field_map_list: list[FieldMap] = []
233
240
  for record in records:
234
- field_map_list.append(AliasUtil.to_field_map(record))
241
+ field_map_list.append(AliasUtil.to_field_map(record, include_record_id))
235
242
  return field_map_list
236
243
 
237
244
  @staticmethod