sapiopycommons 2025.1.7rc401__py3-none-any.whl → 2025.1.20a403__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,50 +1,50 @@
1
1
  from collections.abc import Iterable
2
- from typing import Any
2
+ from typing import Any, TypeAlias
3
3
 
4
4
  from sapiopylib.rest.User import SapioUser
5
5
  from sapiopylib.rest.pojo.DataRecord import DataRecord
6
- from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
6
+ from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType, AbstractVeloxFieldDefinition
7
7
  from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment
8
8
  from sapiopylib.rest.pojo.eln.ExperimentEntry import ExperimentEntry
9
9
  from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnBaseDataType
10
10
  from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
11
11
  from sapiopylib.rest.utils.Protocols import ElnExperimentProtocol, ElnEntryStep
12
- from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
12
+ from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel, AbstractRecordModel
13
13
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedRecordModel, WrappedType, WrapperField
14
14
 
15
15
  from sapiopycommons.general.exceptions import SapioException
16
16
 
17
- FieldValue = int | float | str | bool | None
17
+ FieldValue: TypeAlias = int | float | str | bool | None
18
18
  """Allowable values for fields in the system."""
19
- RecordModel = PyRecordModel | WrappedRecordModel
19
+ RecordModel: TypeAlias = PyRecordModel | AbstractRecordModel | WrappedRecordModel
20
20
  """Different forms that a record model could take."""
21
- SapioRecord = DataRecord | RecordModel
21
+ SapioRecord: TypeAlias = DataRecord | RecordModel
22
22
  """A record could be provided as either a DataRecord, PyRecordModel, or WrappedRecordModel (WrappedType)."""
23
- RecordIdentifier = SapioRecord | int
23
+ RecordIdentifier: TypeAlias = SapioRecord | int
24
24
  """A RecordIdentifier is either a record type or an integer for the record's record ID."""
25
- DataTypeIdentifier = SapioRecord | type[WrappedType] | str
25
+ DataTypeIdentifier: TypeAlias = SapioRecord | type[WrappedType] | str
26
26
  """A DataTypeIdentifier is either a SapioRecord, a record model wrapper type, or a string."""
27
- FieldIdentifier = WrapperField | str | tuple[str, FieldType]
27
+ FieldIdentifier: TypeAlias = AbstractVeloxFieldDefinition | WrapperField | str | tuple[str, FieldType]
28
28
  """A FieldIdentifier is either wrapper field from a record model wrapper, a string, or a tuple of string
29
29
  and field type."""
30
- FieldIdentifierKey = WrapperField | str
30
+ FieldIdentifierKey: TypeAlias = WrapperField | str
31
31
  """A FieldIdentifierKey is a FieldIdentifier, except it can't be a tuple, s tuples can't be used as keys in
32
32
  dictionaries.."""
33
- HasFieldWrappers = type[WrappedType] | WrappedRecordModel
33
+ HasFieldWrappers: TypeAlias = type[WrappedType] | WrappedRecordModel
34
34
  """An identifier for classes that have wrapper fields."""
35
- ExperimentIdentifier = ElnExperimentProtocol | ElnExperiment | int
35
+ ExperimentIdentifier: TypeAlias = ElnExperimentProtocol | ElnExperiment | int
36
36
  """An ExperimentIdentifier is either an experiment protocol, experiment, or an integer for the experiment's notebook
37
37
  ID."""
38
- ExperimentEntryIdentifier = ElnEntryStep | ExperimentEntry | int
38
+ ExperimentEntryIdentifier: TypeAlias = ElnEntryStep | ExperimentEntry | int
39
39
  """An ExperimentEntryIdentifier is either an ELN entry step, experiment entry, or an integer for the entry's ID."""
40
- FieldMap = dict[str, FieldValue]
40
+ FieldMap: TypeAlias = dict[str, FieldValue]
41
41
  """A field map is simply a dict of data field names to values. The purpose of aliasing this is to help distinguish
42
42
  any random dict in a webhook from one which is explicitly used for record fields."""
43
- FieldIdentifierMap = dict[FieldIdentifierKey, FieldValue]
43
+ FieldIdentifierMap: TypeAlias = dict[FieldIdentifierKey, FieldValue]
44
44
  """A field identifier map is the same thing as a field map, except the keys can be field identifiers instead
45
45
  of just strings. Note that although one of the allowed field identifiers is a tuple, you can't use tuples as
46
46
  keys in a dictionary."""
47
- UserIdentifier = SapioWebhookContext | SapioUser
47
+ UserIdentifier: TypeAlias = SapioWebhookContext | SapioUser
48
48
  """An identifier for classes from which a user object can be used for sending requests."""
49
49
 
50
50
 
@@ -142,23 +142,25 @@ class AliasUtil:
142
142
  @staticmethod
143
143
  def to_data_field_name(value: FieldIdentifier) -> str:
144
144
  """
145
- Convert a string or WrapperField to a data field name string.
145
+ Convert an object that can be used to identify a data field to a data field name string.
146
146
 
147
- :param value: A string or WrapperField.
147
+ :param value: An object that can be used to identify a data field.
148
148
  :return: A string of the data field name of the input value.
149
149
  """
150
150
  if isinstance(value, tuple):
151
151
  return value[0]
152
152
  if isinstance(value, WrapperField):
153
153
  return value.field_name
154
+ if isinstance(value, AbstractVeloxFieldDefinition):
155
+ return value.data_field_name
154
156
  return value
155
157
 
156
158
  @staticmethod
157
159
  def to_data_field_names(values: Iterable[FieldIdentifier]) -> list[str]:
158
160
  """
159
- Convert an iterable of strings or WrapperFields to a list of data field name strings.
161
+ Convert an iterable of objects that can be used to identify data fields to a list of data field name strings.
160
162
 
161
- :param values: An iterable of strings or WrapperFields.
163
+ :param values: An iterable of objects that can be used to identify a data field.
162
164
  :return: A list of strings of the data field names of the input values.
163
165
  """
164
166
  return [AliasUtil.to_data_field_name(x) for x in values]
@@ -204,21 +206,32 @@ class AliasUtil:
204
206
  raise SapioException(f"The wrapper of data type \"{data_type.get_wrapper_data_type_name()}\" doesn't have a "
205
207
  f"field with the name \"{field}\",")
206
208
 
209
+ @staticmethod
210
+ def to_field_map(record: SapioRecord) -> FieldMap:
211
+ """
212
+ Convert a given record value to a field map. This includes the given RecordId of the given record.
213
+
214
+ :return: The field map for the input record.
215
+ """
216
+ if isinstance(record, DataRecord):
217
+ # noinspection PyTypeChecker
218
+ fields: FieldMap = record.get_fields()
219
+ else:
220
+ fields: FieldMap = record.fields.copy_to_dict()
221
+ fields["RecordId"] = AliasUtil.to_record_id(record)
222
+ return fields
223
+
207
224
  @staticmethod
208
225
  def to_field_map_lists(records: Iterable[SapioRecord]) -> list[FieldMap]:
209
226
  """
210
- Convert a list of variables that could either be DataRecords, PyRecordModels,
211
- or WrappedRecordModels to a list of their field maps.
227
+ Convert a list of variables that could either be DataRecords, PyRecordModels, or WrappedRecordModels
228
+ to a list of their field maps. This includes the given RecordId of the given records.
212
229
 
213
230
  :return: A list of field maps for the input records.
214
231
  """
215
232
  field_map_list: list[FieldMap] = []
216
233
  for record in records:
217
- if isinstance(record, DataRecord):
218
- # noinspection PyTypeChecker
219
- field_map_list.append(record.get_fields())
220
- else:
221
- field_map_list.append(record.fields.copy_to_dict())
234
+ field_map_list.append(AliasUtil.to_field_map(record))
222
235
  return field_map_list
223
236
 
224
237
  @staticmethod
@@ -0,0 +1,86 @@
1
+ from typing import Iterable, cast
2
+
3
+ from sapiopylib.rest.User import SapioUser
4
+ from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, CustomReport
5
+ from sapiopylib.rest.pojo.webhook.WebhookDirective import HomePageDirective, FormDirective, TableDirective, \
6
+ CustomReportDirective, ElnExperimentDirective, ExperimentEntryDirective
7
+
8
+ from sapiopycommons.general.aliases import SapioRecord, AliasUtil, ExperimentIdentifier, ExperimentEntryIdentifier, \
9
+ UserIdentifier
10
+ from sapiopycommons.general.custom_report_util import CustomReportUtil
11
+
12
+
13
+ # FR-47392: Create a DirectiveUtil class to simplify the creation of directives.
14
+ class DirectiveUtil:
15
+ """
16
+ DirectiveUtil is a class for creating webhook directives. The utility functions reduce the provided variables
17
+ down to the exact type that the directives require, removing the need for the caller to handle the conversion.
18
+ """
19
+ user: SapioUser
20
+
21
+ def __init__(self, context: UserIdentifier):
22
+ """
23
+ :param context: The current webhook context or a user object to send requests from.
24
+ """
25
+ self.user = AliasUtil.to_sapio_user(context)
26
+
27
+ @staticmethod
28
+ def homepage() -> HomePageDirective:
29
+ """
30
+ :return: A directive that sends the user back to their home page.
31
+ """
32
+ return HomePageDirective()
33
+
34
+ @staticmethod
35
+ def record_form(record: SapioRecord) -> FormDirective:
36
+ """
37
+ :param record: A record in the system.
38
+ :return: A directive that sends the user to a specific data record form.
39
+ """
40
+ return FormDirective(AliasUtil.to_data_record(record))
41
+
42
+ @staticmethod
43
+ def record_table(records: Iterable[SapioRecord]) -> TableDirective:
44
+ """
45
+ :param records: A list of records in the system.
46
+ :return: A directive that sends the user to a table of data records.
47
+ """
48
+ return TableDirective(AliasUtil.to_data_records(records))
49
+
50
+ @staticmethod
51
+ def record_adaptive(records: Iterable[SapioRecord]) -> TableDirective | FormDirective:
52
+ """
53
+ :param records: A list of records in the system.
54
+ :return: A directive that sends the user to a table of data records if there are multiple records,
55
+ or a directive that sends the user to a specific data record form if there is only one record.
56
+ """
57
+ records: list[SapioRecord] = list(records)
58
+ if len(records) == 1:
59
+ return DirectiveUtil.record_form(records[0])
60
+ return DirectiveUtil.record_table(records)
61
+
62
+ def custom_report(self, report: CustomReport | CustomReportCriteria | str) -> CustomReportDirective:
63
+ """
64
+ :param report: A custom report, the criteria for a custom report, or the name of a system report.
65
+ :return: A directive that sends the user to the results of the provided custom report.
66
+ """
67
+ if isinstance(report, str):
68
+ report: CustomReport = CustomReportUtil.get_system_report_criteria(self.user, report)
69
+ return CustomReportDirective(cast(CustomReport, report))
70
+
71
+ @staticmethod
72
+ def eln_experiment(experiment: ExperimentIdentifier) -> ElnExperimentDirective:
73
+ """
74
+ :param experiment: An identifier for an experiment.
75
+ :return: A directive that sends the user to the ELN experiment.
76
+ """
77
+ return ElnExperimentDirective(AliasUtil.to_notebook_id(experiment))
78
+
79
+ @staticmethod
80
+ def eln_entry(experiment: ExperimentIdentifier, entry: ExperimentEntryIdentifier) -> ExperimentEntryDirective:
81
+ """
82
+ :param experiment: An identifier for an experiment.
83
+ :param entry: An identifier for an entry in the experiment.
84
+ :return: A directive that sends the user to the provided experiment entry within its ELN experiment.
85
+ """
86
+ return ExperimentEntryDirective(AliasUtil.to_notebook_id(experiment), AliasUtil.to_entry_id(entry))
@@ -1,3 +1,20 @@
1
+ from enum import Enum
2
+
3
+
4
+ class MessageDisplayType(Enum):
5
+ """
6
+ An enum representing the different ways in which a message can be displayed to the user.
7
+ """
8
+ TOASTER_SUCCESS = 0
9
+ TOASTER_INFO = 1
10
+ TOASTER_WARNING = 2
11
+ TOASTER_ERROR = 3
12
+ OK_DIALOG = 4
13
+ DISPLAY_INFO = 5
14
+ DISPLAY_WARNING = 6
15
+ DISPLAY_ERROR = 7
16
+
17
+
1
18
  # FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
2
19
  class SapioException(Exception):
3
20
  """
@@ -29,7 +46,29 @@ class SapioDialogTimeoutException(SapioException):
29
46
  pass
30
47
 
31
48
 
32
- class SapioUserErrorException(SapioException):
49
+ class DisplayableException(SapioException):
50
+ """
51
+ A generic exception that promises to return a user-friendly message explaining the error that should be displayed to
52
+ the user. Note that it is up to whichever class that catches this exception to actually display the message.
53
+ """
54
+ msg: str
55
+ display_type: MessageDisplayType | None
56
+ title: str | None
57
+
58
+ def __init__(self, msg: str, display_type: MessageDisplayType | None = None, title: str | None = None):
59
+ """
60
+ :param msg: The message that should be displayed to the user.
61
+ :param display_type: The manner in which the message should be displayed. If None, then the display type should
62
+ be controlled by the class that catches this exception.
63
+ :param title: If the display type is able to have a title, this is the title that will be displayed. If None,
64
+ then the title should be controlled by the class that catches this exception.
65
+ """
66
+ self.msg = msg
67
+ self.display_type = display_type
68
+ self.title = title
69
+
70
+
71
+ class SapioUserErrorException(DisplayableException):
33
72
  """
34
73
  An exception caused by user error (e.g. user provided a CSV when an XLSX was expected), which promises to return a
35
74
  user-friendly message explaining the error that should be displayed to the user.
@@ -39,7 +78,7 @@ class SapioUserErrorException(SapioException):
39
78
  pass
40
79
 
41
80
 
42
- class SapioCriticalErrorException(SapioException):
81
+ class SapioCriticalErrorException(DisplayableException):
43
82
  """
44
83
  A critical exception caused by user error, which promises to return a user-friendly message explaining the error
45
84
  that should be displayed to the user.