sapiopycommons 2024.11.19a369__tar.gz → 2024.11.22a371__tar.gz

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

Potentially problematic release.


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

Files changed (78) hide show
  1. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/PKG-INFO +1 -1
  2. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/pyproject.toml +1 -1
  3. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/callbacks/field_builder.py +78 -70
  4. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/custom_report_util.py +55 -35
  5. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/recordmodel/record_handler.py +4 -4
  6. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/sftpconnect/sftp_builder.py +1 -0
  7. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/webhook/webhook_handlers.py +8 -6
  8. sapiopycommons-2024.11.22a371/src/sapiopycommons/webhook/webservice_handlers.py +386 -0
  9. sapiopycommons-2024.11.19a369/src/sapiopycommons/webhook/webservice_handlers.py +0 -67
  10. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/.gitignore +0 -0
  11. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/LICENSE +0 -0
  12. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/README.md +0 -0
  13. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/__init__.py +0 -0
  14. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/callbacks/__init__.py +0 -0
  15. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/callbacks/callback_util.py +0 -0
  16. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
  17. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/chem/Molecules.py +0 -0
  18. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/chem/__init__.py +0 -0
  19. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/customreport/__init__.py +0 -0
  20. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/customreport/column_builder.py +0 -0
  21. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/customreport/custom_report_builder.py +0 -0
  22. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/customreport/term_builder.py +0 -0
  23. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/datatype/__init__.py +0 -0
  24. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/datatype/attachment_util.py +0 -0
  25. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/datatype/data_fields.py +0 -0
  26. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/datatype/pseudo_data_types.py +0 -0
  27. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/eln/__init__.py +0 -0
  28. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/eln/experiment_handler.py +0 -0
  29. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/eln/experiment_report_util.py +0 -0
  30. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/eln/plate_designer.py +0 -0
  31. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/files/__init__.py +0 -0
  32. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/files/complex_data_loader.py +0 -0
  33. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/files/file_bridge.py +0 -0
  34. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/files/file_bridge_handler.py +0 -0
  35. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/files/file_data_handler.py +0 -0
  36. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/files/file_util.py +0 -0
  37. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/files/file_validator.py +0 -0
  38. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/files/file_writer.py +0 -0
  39. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/flowcyto/flow_cyto.py +0 -0
  40. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/flowcyto/flowcyto_data.py +0 -0
  41. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/__init__.py +0 -0
  42. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/accession_service.py +0 -0
  43. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/aliases.py +0 -0
  44. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/audit_log.py +0 -0
  45. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/exceptions.py +0 -0
  46. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/popup_util.py +0 -0
  47. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/sapio_links.py +0 -0
  48. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/storage_util.py +0 -0
  49. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/general/time_util.py +0 -0
  50. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/multimodal/multimodal.py +0 -0
  51. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/multimodal/multimodal_data.py +0 -0
  52. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/processtracking/__init__.py +0 -0
  53. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/processtracking/custom_workflow_handler.py +0 -0
  54. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/processtracking/endpoints.py +0 -0
  55. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/recordmodel/__init__.py +0 -0
  56. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/rules/__init__.py +0 -0
  57. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/rules/eln_rule_handler.py +0 -0
  58. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/rules/on_save_rule_handler.py +0 -0
  59. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/sftpconnect/__init__.py +0 -0
  60. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/webhook/__init__.py +0 -0
  61. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/src/sapiopycommons/webhook/webhook_context.py +0 -0
  62. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/AF-A0A009IHW8-F1-model_v4.cif +0 -0
  63. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/_do_not_add_init_py_here +0 -0
  64. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/accession_test.py +0 -0
  65. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/bio_reg_test.py +0 -0
  66. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/chem_test.py +0 -0
  67. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/chem_test_curation_queue.py +0 -0
  68. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/curation_queue_test.sdf +0 -0
  69. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/data_type_models.py +0 -0
  70. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/flowcyto/101_DEN084Y5_15_E01_008_clean.fcs +0 -0
  71. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/flowcyto/101_DEN084Y5_15_E03_009_clean.fcs +0 -0
  72. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/flowcyto/101_DEN084Y5_15_E05_010_clean.fcs +0 -0
  73. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/flowcyto/8_color_ICS.wsp +0 -0
  74. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/flowcyto/COVID19_W_001_O.fcs +0 -0
  75. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/flowcyto_test.py +0 -0
  76. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/kappa.chains.fasta +0 -0
  77. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/mafft_test.py +0 -0
  78. {sapiopycommons-2024.11.19a369 → sapiopycommons-2024.11.22a371}/tests/test.gb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2024.11.19a369
3
+ Version: 2024.11.22a371
4
4
  Summary: Official Sapio Python API Utilities Package
5
5
  Project-URL: Homepage, https://github.com/sapiosciences
6
6
  Author-email: Jonathan Steck <jsteck@sapiosciences.com>, Yechen Qiao <yqiao@sapiosciences.com>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sapiopycommons"
7
- version='2024.11.19a369'
7
+ version='2024.11.22a371'
8
8
  authors = [
9
9
  { name="Jonathan Steck", email="jsteck@sapiosciences.com" },
10
10
  { name="Yechen Qiao", email="yqiao@sapiosciences.com" },
@@ -6,6 +6,7 @@ from sapiopylib.rest.pojo.datatype.FieldDefinition import VeloxStringFieldDefini
6
6
  VeloxLongFieldDefinition, VeloxPickListFieldDefinition, VeloxSelectionFieldDefinition, VeloxShortFieldDefinition, \
7
7
  SapioDoubleFormat, ListMode
8
8
 
9
+ from sapiopycommons.general.aliases import FieldIdentifier, DataTypeIdentifier, AliasUtil
9
10
  from sapiopycommons.general.exceptions import SapioException
10
11
 
11
12
 
@@ -55,16 +56,17 @@ class FieldBuilder:
55
56
  """
56
57
  data_type: str
57
58
 
58
- def __init__(self, data_type: str = "Default"):
59
+ def __init__(self, data_type: DataTypeIdentifier = "Default"):
59
60
  """
60
61
  :param data_type: The data type name that fields created from this builder will use as their data type.
61
62
  """
62
- self.data_type = data_type
63
+ self.data_type = AliasUtil.to_data_type_name(data_type)
63
64
 
64
- def accession_field(self, field_name: str, sequence_key: str, prefix: str | None = None, suffix: str | None = None,
65
- number_of_digits: int = 8, starting_value: int = 1, link_out: dict[str, str] | None = None,
66
- abstract_info: AnyFieldInfo | None = None, *, data_type_name: str | None = None,
67
- display_name: str | None = None) -> VeloxAccessionFieldDefinition:
65
+ def accession_field(self, field_name: FieldIdentifier, sequence_key: str, prefix: str | None = None,
66
+ suffix: str | None = None, number_of_digits: int = 8, starting_value: int = 1,
67
+ link_out: dict[str, str] | None = None, abstract_info: AnyFieldInfo | None = None, *,
68
+ data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
69
+ -> VeloxAccessionFieldDefinition:
68
70
  """
69
71
  Create an accession field definition. Accession fields are text fields which generate a unique value
70
72
  that has not been used before, incrementing from the most recently generated value. This can be used when a
@@ -98,10 +100,10 @@ class FieldBuilder:
98
100
  field name doubles as the display name.
99
101
  :return: An accession field definition with settings from the input criteria.
100
102
  """
103
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
104
+ field_name: str = AliasUtil.to_data_field_name(field_name)
101
105
  if abstract_info is None:
102
106
  abstract_info = AnyFieldInfo()
103
- if not data_type_name:
104
- data_type_name = self.data_type
105
107
  if not display_name:
106
108
  display_name = field_name
107
109
  # Accession fields lock editable to false.
@@ -110,10 +112,11 @@ class FieldBuilder:
110
112
  # The unique parameter has no effect, so just always set it to false.
111
113
  return VeloxAccessionFieldDefinition(data_type_name, field_name, display_name, sequence_key, prefix, suffix,
112
114
  number_of_digits, False, starting_value, link_out, link_out_url,
113
- kwargs=abstract_info.__dict__)
115
+ **abstract_info.__dict__)
114
116
 
115
- def boolean_field(self, field_name: str, default_value: bool | None = False, abstract_info: AnyFieldInfo | None = None,
116
- *, data_type_name: str | None = None, display_name: str | None = None) -> VeloxBooleanFieldDefinition:
117
+ def boolean_field(self, field_name: FieldIdentifier, default_value: bool | None = False,
118
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
119
+ display_name: str | None = None) -> VeloxBooleanFieldDefinition:
117
120
  """
118
121
  Create a boolean field definition. Boolean fields are fields which may have a value of true or false.
119
122
  They appear as a checkbox in the UI. Boolean fields may also have a value of null if the field is not required.
@@ -128,20 +131,21 @@ class FieldBuilder:
128
131
  field name doubles as the display name.
129
132
  :return: A boolean field definition with settings from the input criteria.
130
133
  """
134
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
135
+ field_name: str = AliasUtil.to_data_field_name(field_name)
131
136
  if abstract_info is None:
132
137
  abstract_info = AnyFieldInfo()
133
138
  # Boolean fields assume that they are required if no abstract info is provided.
134
139
  abstract_info.required = True
135
- if not data_type_name:
136
- data_type_name = self.data_type
137
140
  if not display_name:
138
141
  display_name = field_name
139
142
  return VeloxBooleanFieldDefinition(data_type_name, field_name, display_name, default_value,
140
- kwargs=abstract_info.__dict__)
143
+ **abstract_info.__dict__)
141
144
 
142
- def date_field(self, field_name: str, default_value: int | None = None, date_time_format: str = "MMM dd, yyyy",
145
+ def date_field(self, field_name: FieldIdentifier, default_value: int | None = None, date_time_format: str = "MMM dd, yyyy",
143
146
  static_date: bool = False, abstract_info: AnyFieldInfo | None = None, *,
144
- data_type_name: str | None = None, display_name: str | None = None) -> VeloxDateFieldDefinition:
147
+ data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
148
+ -> VeloxDateFieldDefinition:
145
149
  """
146
150
  Create a date field definition. Date fields store date and time information as an integer
147
151
  representing the number of milliseconds since the unix epoch. This timestamp is then displayed to users in a
@@ -162,18 +166,18 @@ class FieldBuilder:
162
166
  field name doubles as the display name.
163
167
  :return: A date field definition with settings from the input criteria.
164
168
  """
169
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
170
+ field_name: str = AliasUtil.to_data_field_name(field_name)
165
171
  if abstract_info is None:
166
172
  abstract_info = AnyFieldInfo()
167
- if not data_type_name:
168
- data_type_name = self.data_type
169
173
  if not display_name:
170
174
  display_name = field_name
171
175
  return VeloxDateFieldDefinition(data_type_name, field_name, display_name, date_time_format, default_value,
172
- static_date, kwargs=abstract_info.__dict__)
176
+ static_date, **abstract_info.__dict__)
173
177
 
174
- def date_range_field(self, field_name: str, default_value: str | DateRange | None = None,
178
+ def date_range_field(self, field_name: FieldIdentifier, default_value: str | DateRange | None = None,
175
179
  date_time_format: str = "MMM dd, yyyy", static_date: bool = False,
176
- abstract_info: AnyFieldInfo | None = None, *, data_type_name: str | None = None,
180
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
177
181
  display_name: str | None = None) -> VeloxDateRangeFieldDefinition:
178
182
  """
179
183
  Create a date range field definition. Date range fields store two unix epoch timestamps as a string of the
@@ -198,20 +202,20 @@ class FieldBuilder:
198
202
  field name doubles as the display name.
199
203
  :return: A date range field definition with settings from the input criteria.
200
204
  """
205
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
206
+ field_name: str = AliasUtil.to_data_field_name(field_name)
201
207
  if abstract_info is None:
202
208
  abstract_info = AnyFieldInfo()
203
- if not data_type_name:
204
- data_type_name = self.data_type
205
209
  if not display_name:
206
210
  display_name = field_name
207
211
  if isinstance(default_value, DateRange):
208
212
  default_value = str(default_value)
209
213
  return VeloxDateRangeFieldDefinition(data_type_name, field_name, display_name, date_time_format, static_date,
210
- default_value, kwargs=abstract_info.__dict__)
214
+ default_value, **abstract_info.__dict__)
211
215
 
212
- def double_field(self, field_name: str, default_value: float | None = None, min_value: float = -10.**120,
216
+ def double_field(self, field_name: FieldIdentifier, default_value: float | None = None, min_value: float = -10.**120,
213
217
  max_value: float = 10.**120, precision: int = 1, double_format: SapioDoubleFormat | None = None,
214
- abstract_info: AnyFieldInfo | None = None, *, data_type_name: str | None = None,
218
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
215
219
  display_name: str | None = None) -> VeloxDoubleFieldDefinition:
216
220
  """
217
221
  Create a double field definition. Double fields represent decimal numerical values. They can also
@@ -232,17 +236,17 @@ class FieldBuilder:
232
236
  field name doubles as the display name.
233
237
  :return: A double field definition with settings from the input criteria.
234
238
  """
239
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
240
+ field_name: str = AliasUtil.to_data_field_name(field_name)
235
241
  if abstract_info is None:
236
242
  abstract_info = AnyFieldInfo()
237
- if not data_type_name:
238
- data_type_name = self.data_type
239
243
  if not display_name:
240
244
  display_name = field_name
241
245
  return VeloxDoubleFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
242
- precision, double_format, kwargs=abstract_info.__dict__)
246
+ precision, double_format, **abstract_info.__dict__)
243
247
 
244
- def enum_field(self, field_name: str, options: list[str], default_value: int | None = None,
245
- abstract_info: AnyFieldInfo | None = None, *, data_type_name: str | None = None,
248
+ def enum_field(self, field_name: FieldIdentifier, options: list[str], default_value: int | None = None,
249
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
246
250
  display_name: str | None = None) -> VeloxEnumFieldDefinition:
247
251
  """
248
252
  Create an enum field definition. Enum fields allow for the display of a list of options as a field
@@ -268,18 +272,19 @@ class FieldBuilder:
268
272
  field name doubles as the display name.
269
273
  :return: An enum field definition with settings from the input criteria.
270
274
  """
275
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
276
+ field_name: str = AliasUtil.to_data_field_name(field_name)
271
277
  if abstract_info is None:
272
278
  abstract_info = AnyFieldInfo()
273
- if not data_type_name:
274
- data_type_name = self.data_type
275
279
  if not display_name:
276
280
  display_name = field_name
277
281
  return VeloxEnumFieldDefinition(data_type_name, field_name, display_name, default_value, options,
278
- kwargs=abstract_info.__dict__)
282
+ **abstract_info.__dict__)
279
283
 
280
- def int_field(self, field_name: str, default_value: int | None = None, min_value: int = -2**31,
284
+ def int_field(self, field_name: FieldIdentifier, default_value: int | None = None, min_value: int = -2**31,
281
285
  max_value: int = 2**31 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None, *,
282
- data_type_name: str | None = None, display_name: str | None = None) -> VeloxIntegerFieldDefinition:
286
+ data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
287
+ -> VeloxIntegerFieldDefinition:
283
288
  """
284
289
  Create an integer field definition. Integer fields are 32-bit whole numbers.
285
290
 
@@ -296,18 +301,19 @@ class FieldBuilder:
296
301
  field name doubles as the display name.
297
302
  :return: An integer field definition with settings from the input criteria.
298
303
  """
304
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
305
+ field_name: str = AliasUtil.to_data_field_name(field_name)
299
306
  if abstract_info is None:
300
307
  abstract_info = AnyFieldInfo()
301
- if not data_type_name:
302
- data_type_name = self.data_type
303
308
  if not display_name:
304
309
  display_name = field_name
305
310
  return VeloxIntegerFieldDefinition(data_type_name, field_name, display_name, min_value, max_value,
306
- default_value, unique_value, kwargs=abstract_info.__dict__)
311
+ default_value, unique_value, **abstract_info.__dict__)
307
312
 
308
- def long_field(self, field_name: str, default_value: int | None = None, min_value: int = -2**63,
313
+ def long_field(self, field_name: FieldIdentifier, default_value: int | None = None, min_value: int = -2**63,
309
314
  max_value: int = 2**63 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None, *,
310
- data_type_name: str | None = None, display_name: str | None = None) -> VeloxLongFieldDefinition:
315
+ data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
316
+ -> VeloxLongFieldDefinition:
311
317
  """
312
318
  Create a long field definition. Long fields are 64-bit whole numbers.
313
319
 
@@ -324,18 +330,19 @@ class FieldBuilder:
324
330
  field name doubles as the display name.
325
331
  :return: A long field definition with settings from the input criteria.
326
332
  """
333
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
334
+ field_name: str = AliasUtil.to_data_field_name(field_name)
327
335
  if abstract_info is None:
328
336
  abstract_info = AnyFieldInfo()
329
- if not data_type_name:
330
- data_type_name = self.data_type
331
337
  if not display_name:
332
338
  display_name = field_name
333
339
  return VeloxLongFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
334
- unique_value, kwargs=abstract_info.__dict__)
340
+ unique_value, **abstract_info.__dict__)
335
341
 
336
- def pick_list_field(self, field_name: str, pick_list_name: str, default_value: str | None = None,
342
+ def pick_list_field(self, field_name: FieldIdentifier, pick_list_name: str, default_value: str | None = None,
337
343
  direct_edit: bool = False, abstract_info: AnyFieldInfo | None = None, *,
338
- data_type_name: str | None = None, display_name: str | None = None) -> VeloxPickListFieldDefinition:
344
+ data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
345
+ -> VeloxPickListFieldDefinition:
339
346
  """
340
347
  Create a pick list field definition. Pick list fields are string fields that display a drop-down list of options
341
348
  when being edited by a user. The list of options is backed by a pick list defined in the list manager sections
@@ -355,22 +362,22 @@ class FieldBuilder:
355
362
  field name doubles as the display name.
356
363
  :return: A pick list field definition with settings from the input criteria.
357
364
  """
365
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
366
+ field_name: str = AliasUtil.to_data_field_name(field_name)
358
367
  if abstract_info is None:
359
368
  abstract_info = AnyFieldInfo()
360
- if not data_type_name:
361
- data_type_name = self.data_type
362
369
  if not display_name:
363
370
  display_name = field_name
364
371
  return VeloxPickListFieldDefinition(data_type_name, field_name, display_name, pick_list_name, default_value,
365
- direct_edit, kwargs=abstract_info.__dict__)
372
+ direct_edit, **abstract_info.__dict__)
366
373
 
367
- def selection_list_field(self, field_name: str, default_value: str | None = None, direct_edit: bool = False,
368
- multi_select: bool = False, unique_value: bool = False,
374
+ def selection_list_field(self, field_name: FieldIdentifier, default_value: str | None = None,
375
+ direct_edit: bool = False, multi_select: bool = False, unique_value: bool = False,
369
376
  abstract_info: AnyFieldInfo | None = None, *, pick_list_name: str | None = None,
370
377
  custom_report_name: str | None = None, plugin_name: str | None = None,
371
378
  static_values: list[str] | None = None, user_list: bool = False,
372
379
  user_group_list: bool = False, non_api_user_list: bool = False,
373
- data_type_name: str | None = None, display_name: str | None = None) \
380
+ data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
374
381
  -> VeloxSelectionFieldDefinition:
375
382
  """
376
383
  Create a selection list field definition. Selection list fields are string fields that display a drop-down list
@@ -401,10 +408,10 @@ class FieldBuilder:
401
408
  field name doubles as the display name.
402
409
  :return: A selection list field definition with settings from the input criteria.
403
410
  """
411
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
412
+ field_name: str = AliasUtil.to_data_field_name(field_name)
404
413
  if abstract_info is None:
405
414
  abstract_info = AnyFieldInfo()
406
- if not data_type_name:
407
- data_type_name = self.data_type
408
415
  if not display_name:
409
416
  display_name = field_name
410
417
 
@@ -443,11 +450,12 @@ class FieldBuilder:
443
450
  list_mode, unique_value, multi_select,
444
451
  default_value, pick_list_name, custom_report_name,
445
452
  plugin_name, direct_edit, static_values,
446
- kwargs=abstract_info.__dict__)
453
+ **abstract_info.__dict__)
447
454
 
448
- def short_field(self, field_name: str, default_value: int | None = None, min_value: int = -2**15,
455
+ def short_field(self, field_name: FieldIdentifier, default_value: int | None = None, min_value: int = -2**15,
449
456
  max_value: int = 2**15 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None,
450
- *, data_type_name: str | None = None, display_name: str | None = None) -> VeloxShortFieldDefinition:
457
+ *, data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
458
+ -> VeloxShortFieldDefinition:
451
459
  """
452
460
  Create a short field definition. Short fields are 16-bit whole numbers.
453
461
 
@@ -464,21 +472,21 @@ class FieldBuilder:
464
472
  field name doubles as the display name.
465
473
  :return: A short field definition with settings from the input criteria.
466
474
  """
475
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
476
+ field_name: str = AliasUtil.to_data_field_name(field_name)
467
477
  if abstract_info is None:
468
478
  abstract_info = AnyFieldInfo()
469
- if not data_type_name:
470
- data_type_name = self.data_type
471
479
  if not display_name:
472
480
  display_name = field_name
473
481
  return VeloxShortFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
474
- unique_value, kwargs=abstract_info.__dict__)
475
-
476
- def string_field(self, field_name: str,
477
- default_value: str | None = None, max_length: int = 100, unique_value: bool = False,
478
- html_editor: bool = False, string_format: SapioStringFormat | None = None, num_lines: int = 1,
479
- auto_size: bool = False, link_out: dict[str, str] | None = None,
480
- field_validator: FieldValidator | None = None, abstract_info: AnyFieldInfo | None = None, *,
481
- data_type_name: str | None = None, display_name: str | None = None) -> VeloxStringFieldDefinition:
482
+ unique_value, **abstract_info.__dict__)
483
+
484
+ def string_field(self, field_name: FieldIdentifier, default_value: str | None = None, max_length: int = 100,
485
+ unique_value: bool = False, html_editor: bool = False,
486
+ string_format: SapioStringFormat | None = None, num_lines: int = 1, auto_size: bool = False,
487
+ link_out: dict[str, str] | None = None, field_validator: FieldValidator | None = None,
488
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
489
+ display_name: str | None = None) -> VeloxStringFieldDefinition:
482
490
  """
483
491
  Create a string field definition. String fields represent text, and are highly customizable, allowing the
484
492
  field to be plain text or rich HTML, take up one line of space or multiple on a form, format as emails or
@@ -516,16 +524,16 @@ class FieldBuilder:
516
524
  field name doubles as the display name.
517
525
  :return: A string field definition with settings from the input criteria.
518
526
  """
527
+ data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
528
+ field_name: str = AliasUtil.to_data_field_name(field_name)
519
529
  if abstract_info is None:
520
530
  abstract_info = AnyFieldInfo()
521
- if not data_type_name:
522
- data_type_name = self.data_type
523
531
  if not display_name:
524
532
  display_name = field_name
525
533
  link_out, link_out_url = self._convert_link_out(link_out)
526
534
  return VeloxStringFieldDefinition(data_type_name, field_name, display_name, default_value, max_length,
527
535
  unique_value, html_editor, string_format, num_lines, auto_size, link_out,
528
- link_out_url, field_validator, kwargs=abstract_info.__dict__)
536
+ link_out_url, field_validator, **abstract_info.__dict__)
529
537
 
530
538
  @staticmethod
531
539
  def _convert_link_out(link_out: dict[str, str] | None) -> tuple[bool, str | None]:
@@ -29,8 +29,10 @@ class CustomReportUtil:
29
29
  filter on. Only those headers that both the filters and the custom report share will take effect. That is,
30
30
  any filters that have a header name that isn't in the custom report will be ignored.
31
31
  :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
32
- :param page_size: The size of each page of results in the search. If None, the page size is set by the server.
33
- :param page_number: The page number to start the search from, If None, starts on the first page.
32
+ :param page_size: The size of each page of results in the search. If None, the page size is set by the server,
33
+ which may be unlimited.
34
+ :param page_number: The page number to start the search from, If None, starts on the first page. Note that the
35
+ number of the first page is 0.
34
36
  :return: The results of the report listed row by row, mapping each cell to the header it is under. The header
35
37
  values in the dicts are the data field names of the columns.
36
38
  If two columns in the search have the same data field name but differing data type names, then the
@@ -38,11 +40,11 @@ class CustomReportUtil:
38
40
  had a Sample column with a data field name of Identifier and a Request column with the same data field name,
39
41
  then the dictionary keys for these columns would be Sample.Identifier and Request.Identifier respectively.
40
42
  """
41
- results: tuple = CustomReportUtil.__exhaust_system_report(context, report_name, page_limit,
42
- page_size, page_number)
43
+ results: tuple = CustomReportUtil._exhaust_system_report(context, report_name, page_limit,
44
+ page_size, page_number)
43
45
  columns: list[ReportColumn] = results[0]
44
46
  rows: list[list[FieldValue]] = results[1]
45
- return CustomReportUtil.__process_results(rows, columns, filters)
47
+ return CustomReportUtil._process_results(rows, columns, filters)
46
48
 
47
49
  @staticmethod
48
50
  def run_custom_report(context: UserIdentifier,
@@ -71,7 +73,8 @@ class CustomReportUtil:
71
73
  :param page_size: The size of each page of results in the search. If None, uses the value from the given report
72
74
  criteria. If not None, overwrites the value from the given report criteria.
73
75
  :param page_number: The page number to start the search from, If None, uses the value from the given report
74
- criteria. If not None, overwrites the value from the given report criteria.
76
+ criteria. If not None, overwrites the value from the given report criteria. Note that the number of the
77
+ first page is 0.
75
78
  :return: The results of the report listed row by row, mapping each cell to the header it is under. The header
76
79
  values in the dicts are the data field names of the columns.
77
80
  If two columns in the search have the same data field name but differing data type names, then the
@@ -79,11 +82,11 @@ class CustomReportUtil:
79
82
  had a Sample column with a data field name of Identifier and a Request column with the same data field name,
80
83
  then the dictionary keys for these columns would be Sample.Identifier and Request.Identifier respectively.
81
84
  """
82
- results: tuple = CustomReportUtil.__exhaust_custom_report(context, report_criteria, page_limit,
83
- page_size, page_number)
85
+ results: tuple = CustomReportUtil._exhaust_custom_report(context, report_criteria, page_limit,
86
+ page_size, page_number)
84
87
  columns: list[ReportColumn] = results[0]
85
88
  rows: list[list[FieldValue]] = results[1]
86
- return CustomReportUtil.__process_results(rows, columns, filters)
89
+ return CustomReportUtil._process_results(rows, columns, filters)
87
90
 
88
91
  @staticmethod
89
92
  def run_quick_report(context: UserIdentifier,
@@ -107,16 +110,18 @@ class CustomReportUtil:
107
110
  filter on. Only those headers that both the filters and the custom report share will take effect. That is,
108
111
  any filters that have a header name that isn't in the custom report will be ignored.
109
112
  :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
110
- :param page_size: The size of each page of results in the search. If None, the page size is set by the server.
111
- :param page_number: The page number to start the search from, If None, starts on the first page.
113
+ :param page_size: The size of each page of results in the search. If None, the page size is set by the server,
114
+ which may be unlimited.
115
+ :param page_number: The page number to start the search from, If None, starts on the first page. Note that the
116
+ number of the first page is 0.
112
117
  :return: The results of the report listed row by row, mapping each cell to the header it is under. The header
113
118
  values in the dicts are the data field names of the columns.
114
119
  """
115
- results: tuple = CustomReportUtil.__exhaust_quick_report(context, report_term, page_limit,
116
- page_size, page_number)
120
+ results: tuple = CustomReportUtil._exhaust_quick_report(context, report_term, page_limit,
121
+ page_size, page_number)
117
122
  columns: list[ReportColumn] = results[0]
118
123
  rows: list[list[FieldValue]] = results[1]
119
- return CustomReportUtil.__process_results(rows, columns, filters)
124
+ return CustomReportUtil._process_results(rows, columns, filters)
120
125
 
121
126
  @staticmethod
122
127
  def get_system_report_criteria(context: UserIdentifier, report_name: str) -> CustomReport:
@@ -136,14 +141,14 @@ class CustomReportUtil:
136
141
  """
137
142
  user: SapioUser = AliasUtil.to_sapio_user(context)
138
143
  report_man = DataMgmtServer.get_custom_report_manager(user)
139
- return report_man.run_system_report_by_name(report_name, 1, 1)
144
+ return report_man.run_system_report_by_name(report_name, 1, 0)
140
145
 
141
146
  @staticmethod
142
- def __exhaust_system_report(context: UserIdentifier,
143
- report_name: str,
144
- page_limit: int | None,
145
- page_size: int | None,
146
- page_number: int | None) \
147
+ def _exhaust_system_report(context: UserIdentifier,
148
+ report_name: str,
149
+ page_limit: int | None,
150
+ page_size: int | None,
151
+ page_number: int | None) \
147
152
  -> tuple[list[ReportColumn], list[list[FieldValue]]]:
148
153
  """
149
154
  Given a system report, iterate over every page of the report and collect the results
@@ -152,6 +157,11 @@ class CustomReportUtil:
152
157
  user: SapioUser = AliasUtil.to_sapio_user(context)
153
158
  report_man = DataMgmtServer.get_custom_report_manager(user)
154
159
 
160
+ # If a page size was provided but no page number was provided, then set the page number to 0,
161
+ # as both parameters are necessary in order to get paged results.
162
+ if page_size is not None and page_number is None:
163
+ page_number = 0
164
+
155
165
  result = None
156
166
  has_next_page: bool = True
157
167
  rows: list[list[FieldValue]] = []
@@ -159,18 +169,18 @@ class CustomReportUtil:
159
169
  while has_next_page and (not page_limit or cur_page <= page_limit):
160
170
  result = report_man.run_system_report_by_name(report_name, page_size, page_number)
161
171
  page_size = result.page_size
162
- page_number = result.page_number
172
+ page_number = result.page_number + 1
163
173
  has_next_page = result.has_next_page
164
174
  rows.extend(result.result_table)
165
175
  cur_page += 1
166
176
  return result.column_list, rows
167
177
 
168
178
  @staticmethod
169
- def __exhaust_custom_report(context: UserIdentifier,
170
- report: CustomReportCriteria,
171
- page_limit: int | None,
172
- page_size: int | None,
173
- page_number: int | None) \
179
+ def _exhaust_custom_report(context: UserIdentifier,
180
+ report: CustomReportCriteria,
181
+ page_limit: int | None,
182
+ page_size: int | None,
183
+ page_number: int | None) \
174
184
  -> tuple[list[ReportColumn], list[list[FieldValue]]]:
175
185
  """
176
186
  Given a custom report, iterate over every page of the report and collect the results
@@ -179,6 +189,11 @@ class CustomReportUtil:
179
189
  user: SapioUser = AliasUtil.to_sapio_user(context)
180
190
  report_man = DataMgmtServer.get_custom_report_manager(user)
181
191
 
192
+ # If a page size was provided but no page number was provided, then set the page number to 0,
193
+ # as both parameters are necessary in order to get paged results.
194
+ if page_size is not None and page_number is None:
195
+ page_number = 0
196
+
182
197
  result = None
183
198
  if page_size is not None:
184
199
  report.page_size = page_size
@@ -190,18 +205,18 @@ class CustomReportUtil:
190
205
  while has_next_page and (not page_limit or cur_page <= page_limit):
191
206
  result = report_man.run_custom_report(report)
192
207
  report.page_size = result.page_size
193
- report.page_number = result.page_number
208
+ report.page_number = result.page_number + 1
194
209
  has_next_page = result.has_next_page
195
210
  rows.extend(result.result_table)
196
211
  cur_page += 1
197
212
  return result.column_list, rows
198
213
 
199
214
  @staticmethod
200
- def __exhaust_quick_report(context: UserIdentifier,
201
- report_term: RawReportTerm,
202
- page_limit: int | None,
203
- page_size: int | None,
204
- page_number: int | None) \
215
+ def _exhaust_quick_report(context: UserIdentifier,
216
+ report_term: RawReportTerm,
217
+ page_limit: int | None,
218
+ page_size: int | None,
219
+ page_number: int | None) \
205
220
  -> tuple[list[ReportColumn], list[list[FieldValue]]]:
206
221
  """
207
222
  Given a quick report, iterate over every page of the report and collect the results
@@ -210,6 +225,11 @@ class CustomReportUtil:
210
225
  user: SapioUser = AliasUtil.to_sapio_user(context)
211
226
  report_man = DataMgmtServer.get_custom_report_manager(user)
212
227
 
228
+ # If a page size was provided but no page number was provided, then set the page number to 0,
229
+ # as both parameters are necessary in order to get paged results.
230
+ if page_size is not None and page_number is None:
231
+ page_number = 0
232
+
213
233
  result = None
214
234
  has_next_page: bool = True
215
235
  rows: list[list[FieldValue]] = []
@@ -217,15 +237,15 @@ class CustomReportUtil:
217
237
  while has_next_page and (not page_limit or cur_page <= page_limit):
218
238
  result = report_man.run_quick_report(report_term, page_size, page_number)
219
239
  page_size = result.page_size
220
- page_number = result.page_number
240
+ page_number = result.page_number + 1
221
241
  has_next_page = result.has_next_page
222
242
  rows.extend(result.result_table)
223
243
  cur_page += 1
224
244
  return result.column_list, rows
225
245
 
226
246
  @staticmethod
227
- def __process_results(rows: list[list[FieldValue]], columns: list[ReportColumn],
228
- filters: dict[FieldIdentifierKey, Iterable[FieldValue]] | None) -> list[dict[str, FieldValue]]:
247
+ def _process_results(rows: list[list[FieldValue]], columns: list[ReportColumn],
248
+ filters: dict[FieldIdentifierKey, Iterable[FieldValue]] | None) -> list[dict[str, FieldValue]]:
229
249
  """
230
250
  Given the results of a report as a list of row values and the report's columns, combine these lists to
231
251
  result in a singular list of dictionaries for each row in the results.
@@ -297,7 +297,7 @@ class RecordHandler:
297
297
  not None, in which case it overwrites the given report's value.
298
298
  :param page_number: The page number to start the search from, If None, starts on the first page.
299
299
  If the input report is a custom report criteria, uses the value from the criteria, unless this value is
300
- not None, in which case it overwrites the given report's value.
300
+ not None, in which case it overwrites the given report's value. Note that the number of the first page is 0.
301
301
  :return: The record models for the queried records that matched the given report.
302
302
  """
303
303
  if isinstance(report_name, str):
@@ -966,7 +966,7 @@ class RecordHandler:
966
966
  current = current.get_forward_side_link(node.data_field_name)
967
967
  elif direction == RelationshipNodeType.REVERSE_SIDE_LINK:
968
968
  field_name: str = node.data_field_name
969
- reverse_links: list[PyRecordModel] = current.get_reverse_side_link(field_name, data_type)
969
+ reverse_links: list[PyRecordModel] = current.get_reverse_side_link(data_type, field_name)
970
970
  if not reverse_links:
971
971
  current = None
972
972
  elif len(reverse_links) > 1:
@@ -1016,7 +1016,7 @@ class RecordHandler:
1016
1016
  elif direction == RelationshipNodeType.FORWARD_SIDE_LINK:
1017
1017
  next_search.add(search.get_forward_side_link(node.data_field_name))
1018
1018
  elif direction == RelationshipNodeType.REVERSE_SIDE_LINK:
1019
- next_search.update(search.get_reverse_side_link(node.data_field_name, data_type))
1019
+ next_search.update(search.get_reverse_side_link(data_type, node.data_field_name))
1020
1020
  else:
1021
1021
  raise SapioException("Unsupported path direction.")
1022
1022
  current_search = next_search
@@ -1064,7 +1064,7 @@ class RecordHandler:
1064
1064
  elif direction == RelationshipNodeType.FORWARD_SIDE_LINK:
1065
1065
  current = [current[0].get_forward_side_link(node.data_field_name)]
1066
1066
  elif direction == RelationshipNodeType.REVERSE_SIDE_LINK:
1067
- current = current[0].get_reverse_side_link(node.data_field_name, data_type)
1067
+ current = current[0].get_reverse_side_link(data_type, node.data_field_name)
1068
1068
  else:
1069
1069
  raise SapioException("Unsupported path direction.")
1070
1070
  ret_dict.update({model: self.inst_man.wrap(current[0], wrapper_type) if current else None})
@@ -4,6 +4,7 @@ from enum import Enum
4
4
  import paramiko
5
5
  from paramiko import pkey
6
6
  from paramiko.sftp_client import SFTPClient
7
+
7
8
  from sapiopycommons.general.exceptions import SapioException
8
9
 
9
10