sapiopycommons 2024.8.28a315__py3-none-any.whl → 2024.8.29a317__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.
Files changed (33) hide show
  1. sapiopycommons/callbacks/callback_util.py +37 -133
  2. sapiopycommons/datatype/attachment_util.py +10 -11
  3. sapiopycommons/eln/experiment_handler.py +48 -209
  4. sapiopycommons/eln/experiment_report_util.py +129 -33
  5. sapiopycommons/files/complex_data_loader.py +4 -5
  6. sapiopycommons/files/file_bridge.py +14 -15
  7. sapiopycommons/files/file_bridge_handler.py +5 -27
  8. sapiopycommons/files/file_data_handler.py +5 -2
  9. sapiopycommons/files/file_util.py +5 -38
  10. sapiopycommons/files/file_validator.py +11 -26
  11. sapiopycommons/files/file_writer.py +15 -44
  12. sapiopycommons/general/aliases.py +3 -147
  13. sapiopycommons/general/custom_report_util.py +32 -34
  14. sapiopycommons/general/popup_util.py +0 -17
  15. sapiopycommons/general/time_util.py +0 -40
  16. sapiopycommons/multimodal/multimodal_data.py +1 -0
  17. sapiopycommons/processtracking/endpoints.py +22 -22
  18. sapiopycommons/recordmodel/record_handler.py +77 -228
  19. sapiopycommons/rules/eln_rule_handler.py +25 -34
  20. sapiopycommons/rules/on_save_rule_handler.py +31 -34
  21. sapiopycommons/webhook/webhook_handlers.py +26 -90
  22. {sapiopycommons-2024.8.28a315.dist-info → sapiopycommons-2024.8.29a317.dist-info}/METADATA +1 -1
  23. sapiopycommons-2024.8.29a317.dist-info/RECORD +43 -0
  24. sapiopycommons/customreport/__init__.py +0 -0
  25. sapiopycommons/customreport/column_builder.py +0 -60
  26. sapiopycommons/customreport/custom_report_builder.py +0 -125
  27. sapiopycommons/customreport/term_builder.py +0 -299
  28. sapiopycommons/general/audit_log.py +0 -196
  29. sapiopycommons/general/sapio_links.py +0 -50
  30. sapiopycommons/webhook/webservice_handlers.py +0 -67
  31. sapiopycommons-2024.8.28a315.dist-info/RECORD +0 -50
  32. {sapiopycommons-2024.8.28a315.dist-info → sapiopycommons-2024.8.29a317.dist-info}/WHEEL +0 -0
  33. {sapiopycommons-2024.8.28a315.dist-info → sapiopycommons-2024.8.29a317.dist-info}/licenses/LICENSE +0 -0
@@ -1,15 +1,10 @@
1
- from __future__ import annotations
2
-
3
- from weakref import WeakValueDictionary
4
-
5
- from sapiopylib.rest.User import SapioUser
6
1
  from sapiopylib.rest.pojo.DataRecord import DataRecord
7
- from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnBaseDataType
2
+ from sapiopylib.rest.pojo.webhook.VeloxRules import VeloxRuleType, VeloxRuleParser
8
3
  from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
9
4
  from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelManager, RecordModelInstanceManager
10
5
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
11
6
 
12
- from sapiopycommons.general.aliases import FieldMap, AliasUtil, DataTypeIdentifier
7
+ from sapiopycommons.general.aliases import FieldMap
13
8
  from sapiopycommons.general.exceptions import SapioException
14
9
 
15
10
 
@@ -17,6 +12,7 @@ from sapiopycommons.general.exceptions import SapioException
17
12
  class ElnRuleHandler:
18
13
  """
19
14
  A class which helps with the parsing and navigation of the ELN rule result map of a webhook context.
15
+ TODO: Add functionality around the VeloxRuleType of the rule results.
20
16
  """
21
17
  __context: SapioWebhookContext
22
18
  """The context that this handler is working from."""
@@ -38,25 +34,7 @@ class ElnRuleHandler:
38
34
  """A mapping of entry name to the lists of field maps for that entry, each grouping of field maps being mapped by
39
35
  its data type."""
40
36
 
41
- __instances: WeakValueDictionary[SapioUser, ElnRuleHandler] = WeakValueDictionary()
42
- __initialized: bool
43
-
44
- def __new__(cls, context: SapioWebhookContext):
45
- if context.velox_eln_rule_result_map is None:
46
- raise SapioException("No Velox ELN rule result map in context for ElnRuleHandler to parse.")
47
- user = context if isinstance(context, SapioUser) else context.user
48
- obj = cls.__instances.get(user)
49
- if not obj:
50
- obj = object.__new__(cls)
51
- obj.__initialized = False
52
- cls.__instances[user] = obj
53
- return obj
54
-
55
37
  def __init__(self, context: SapioWebhookContext):
56
- if self.__initialized:
57
- return
58
- self.__initialized = True
59
-
60
38
  if context.velox_eln_rule_result_map is None:
61
39
  raise SapioException("No Velox ELN rule result map in context for ElnRuleHandler to parse.")
62
40
  self.__context = context
@@ -86,8 +64,13 @@ class ElnRuleHandler:
86
64
  # Get the data type of this record. If this is an ELN type, ignore the digits.
87
65
  data_type: str = record.data_type_name
88
66
  # PR-46331: Ensure that all ELN types are converted to their base data type name.
89
- if ElnBaseDataType.is_eln_type(data_type):
90
- data_type = ElnBaseDataType.get_base_type(data_type).data_type_name
67
+ # TODO: Use ElnBaseDataType.is_eln_type when it is no longer bugged in sapiopylib.
68
+ if data_type.startswith("ELNExperiment_"):
69
+ data_type = "ELNExperiment"
70
+ elif data_type.startswith("ELNExperimentDetail_"):
71
+ data_type = "ELNExperimentDetail"
72
+ elif data_type.startswith("ELNSampleDetail_"):
73
+ data_type = "ELNSampleDetail"
91
74
  # Update the list of records of this type that exist so far globally.
92
75
  self.__records.setdefault(data_type, set()).add(record)
93
76
  # Do the same for the list of records of this type for this specific entry.
@@ -102,9 +85,19 @@ class ElnRuleHandler:
102
85
  entry_dict: dict[str, dict[int, FieldMap]] = {}
103
86
  for record_result in entry_results:
104
87
  for result in record_result.velox_type_rule_field_map_result_list:
105
- data_type: str = result.velox_type_pojo.data_type_name
106
- if ElnBaseDataType.is_eln_type(data_type):
107
- data_type = ElnBaseDataType.get_base_type(data_type).data_type_name
88
+ # TODO: sapiopylib currently has a bug where this velox_type_pojo variable is stored as a dict instead
89
+ # of as the intended VeloxRuleType object. Parse that dict as a VeloxRuleType before use.
90
+ velox_type: VeloxRuleType | dict = result.velox_type_pojo
91
+ if isinstance(velox_type, dict):
92
+ velox_type: VeloxRuleType = VeloxRuleParser.parse_velox_rule_type(velox_type)
93
+ data_type: str = velox_type.data_type_name
94
+ # TODO: Use ElnBaseDataType.is_eln_type when it is no longer bugged in sapiopylib.
95
+ if data_type.startswith("ELNExperiment_"):
96
+ data_type = "ELNExperiment"
97
+ elif data_type.startswith("ELNExperimentDetail_"):
98
+ data_type = "ELNExperimentDetail"
99
+ elif data_type.startswith("ELNSampleDetail_"):
100
+ data_type = "ELNSampleDetail"
108
101
  for field_map in result.field_map_list:
109
102
  rec_id: int = field_map.get("RecordId")
110
103
  self.__field_maps.setdefault(data_type, {}).update({rec_id: field_map})
@@ -125,7 +118,7 @@ class ElnRuleHandler:
125
118
  """
126
119
  return list(self.__entry_to_field_maps.keys())
127
120
 
128
- def get_records(self, data_type: DataTypeIdentifier, entry: str | None = None) -> list[DataRecord]:
121
+ def get_records(self, data_type: str, entry: str | None = None) -> list[DataRecord]:
129
122
  """
130
123
  Get records from the cached context with the given data type. Capable of being filtered to searching within
131
124
  the context of an entry name. If the given data type or entry does not exist in the context,
@@ -136,12 +129,11 @@ class ElnRuleHandler:
136
129
  type from every entry. If an entry is provided, but it does not exist in the context, returns an empty list.
137
130
  :return: The records from the context that match the input parameters.
138
131
  """
139
- data_type: str = AliasUtil.to_data_type_name(data_type)
140
132
  records: dict[str, set[DataRecord]] = self.__entry_to_records.get(entry, {}) if entry else self.__records
141
133
  return list(records.get(data_type, []))
142
134
 
143
135
  # FR-46701: Add functions to the rule handlers for accessing the field maps of inaccessible records in the context.
144
- def get_field_maps(self, data_type: DataTypeIdentifier, entry: str | None = None) -> list[FieldMap]:
136
+ def get_field_maps(self, data_type: str, entry: str | None = None) -> list[FieldMap]:
145
137
  """
146
138
  Get field maps from the cached context with the given data type. Capable of being filtered to searching within
147
139
  the context of an entry name. If the given data type or entry does not exist in the context,
@@ -157,7 +149,6 @@ class ElnRuleHandler:
157
149
  list.
158
150
  :return: The field maps from the context that match the input parameters.
159
151
  """
160
- data_type: str = AliasUtil.to_data_type_name(data_type)
161
152
  field_maps: dict[str, dict[int, FieldMap]] = self.__entry_to_field_maps.get(entry, {}) if entry else self.__field_maps
162
153
  return list(field_maps.get(data_type, {}).values())
163
154
 
@@ -1,15 +1,10 @@
1
- from __future__ import annotations
2
-
3
- from weakref import WeakValueDictionary
4
-
5
- from sapiopylib.rest.User import SapioUser
6
1
  from sapiopylib.rest.pojo.DataRecord import DataRecord
7
- from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnBaseDataType
2
+ from sapiopylib.rest.pojo.webhook.VeloxRules import VeloxRuleType, VeloxRuleParser
8
3
  from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
9
4
  from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelManager, RecordModelInstanceManager
10
5
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
11
6
 
12
- from sapiopycommons.general.aliases import FieldMap, DataTypeIdentifier, AliasUtil
7
+ from sapiopycommons.general.aliases import FieldMap
13
8
  from sapiopycommons.general.exceptions import SapioException
14
9
 
15
10
 
@@ -17,6 +12,7 @@ from sapiopycommons.general.exceptions import SapioException
17
12
  class OnSaveRuleHandler:
18
13
  """
19
14
  A class which helps with the parsing and navigation of the on save rule result map of a webhook context.
15
+ TODO: Add functionality around the VeloxRuleType of the rule results.
20
16
  """
21
17
  __context: SapioWebhookContext
22
18
  """The context that this handler is working from."""
@@ -38,25 +34,7 @@ class OnSaveRuleHandler:
38
34
  """A mapping of record IDs of records in the context.data_record_list to the field maps related to that
39
35
  record, each grouping of field maps being mapped by its data type."""
40
36
 
41
- __instances: WeakValueDictionary[SapioUser, OnSaveRuleHandler] = WeakValueDictionary()
42
- __initialized: bool
43
-
44
- def __new__(cls, context: SapioWebhookContext):
45
- if context.velox_on_save_result_map is None:
46
- raise SapioException("No Velox on save rule result map in context for OnSaveRuleHandler to parse.")
47
- user = context if isinstance(context, SapioUser) else context.user
48
- obj = cls.__instances.get(user)
49
- if not obj:
50
- obj = object.__new__(cls)
51
- obj.__initialized = False
52
- cls.__instances[user] = obj
53
- return obj
54
-
55
37
  def __init__(self, context: SapioWebhookContext):
56
- if self.__initialized:
57
- return
58
- self.__initialized = True
59
-
60
38
  if context.velox_on_save_result_map is None:
61
39
  raise SapioException("No Velox on save rule result map in context for OnSaveRuleHandler to parse.")
62
40
  self.__context = context
@@ -73,6 +51,9 @@ class OnSaveRuleHandler:
73
51
  self.__base_id_to_records = {}
74
52
  # Each record ID in the context has a list of results for that record.
75
53
  for record_id, rule_results in self.__context.velox_on_save_result_map.items():
54
+ # TODO: Record IDs are currently being stored in the map as strings instead of ints. This can be removed
55
+ # once sapiopylib is fixed.
56
+ record_id = int(record_id)
76
57
  # Keep track of the records for this specific record ID.
77
58
  id_dict: dict[str, set[DataRecord]] = {}
78
59
  # The list of results for a record consist of a list of data records and a VeloxType that specifies
@@ -83,8 +64,13 @@ class OnSaveRuleHandler:
83
64
  # Get the data type of this record. If this is an ELN type, ignore the digits.
84
65
  data_type: str = record.data_type_name
85
66
  # PR-46331: Ensure that all ELN types are converted to their base data type name.
86
- if ElnBaseDataType.is_eln_type(data_type):
87
- data_type = ElnBaseDataType.get_base_type(data_type).data_type_name
67
+ # TODO: Use ElnBaseDataType.is_eln_type when it is no longer bugged in sapiopylib.
68
+ if data_type.startswith("ELNExperiment_"):
69
+ data_type = "ELNExperiment"
70
+ elif data_type.startswith("ELNExperimentDetail_"):
71
+ data_type = "ELNExperimentDetail"
72
+ elif data_type.startswith("ELNSampleDetail_"):
73
+ data_type = "ELNSampleDetail"
88
74
  # Update the list of records of this type that exist so far globally.
89
75
  self.__records.setdefault(data_type, set()).add(record)
90
76
  # Do the same for the list of records of this type that relate to this record ID.
@@ -96,11 +82,24 @@ class OnSaveRuleHandler:
96
82
  self.__base_id_to_field_maps = {}
97
83
  # Repeat the same thing for the field map results.
98
84
  for record_id, rule_results in self.__context.velox_on_save_field_map_result_map.items():
85
+ # TODO: Record IDs are currently being stored in the map as strings instead of ints. This can be removed
86
+ # once sapiopylib is fixed.
87
+ record_id = int(record_id)
99
88
  id_dict: dict[str, dict[int, FieldMap]] = {}
100
89
  for record_result in rule_results:
101
- data_type: str = record_result.velox_type_pojo.data_type_name
102
- if ElnBaseDataType.is_eln_type(data_type):
103
- data_type = ElnBaseDataType.get_base_type(data_type).data_type_name
90
+ # TODO: sapiopylib currently has a bug where this velox_type_pojo variable is stored as a dict instead
91
+ # of as the intended VeloxRuleType object. Parse that dict as a VeloxRuleType before use.
92
+ velox_type: VeloxRuleType | dict = record_result.velox_type_pojo
93
+ if isinstance(velox_type, dict):
94
+ velox_type: VeloxRuleType = VeloxRuleParser.parse_velox_rule_type(velox_type)
95
+ data_type: str = velox_type.data_type_name
96
+ # TODO: Use ElnBaseDataType.is_eln_type when it is no longer bugged in sapiopylib.
97
+ if data_type.startswith("ELNExperiment_"):
98
+ data_type = "ELNExperiment"
99
+ elif data_type.startswith("ELNExperimentDetail_"):
100
+ data_type = "ELNExperimentDetail"
101
+ elif data_type.startswith("ELNSampleDetail_"):
102
+ data_type = "ELNSampleDetail"
104
103
  for field_map in record_result.field_map_list:
105
104
  rec_id: int = field_map.get("RecordId")
106
105
  self.__field_maps.setdefault(data_type, {}).update({rec_id: field_map})
@@ -121,7 +120,7 @@ class OnSaveRuleHandler:
121
120
  """
122
121
  return list(self.__base_id_to_field_maps.keys())
123
122
 
124
- def get_records(self, data_type: DataTypeIdentifier, record_id: int | None = None) -> list[DataRecord]:
123
+ def get_records(self, data_type: str, record_id: int | None = None) -> list[DataRecord]:
125
124
  """
126
125
  Get records from the cached context with the given data type. Capable of being filtered to searching within
127
126
  the context of a record ID. If the given data type or record ID does not exist in the context,
@@ -132,12 +131,11 @@ class OnSaveRuleHandler:
132
131
  data type from every ID. If an ID is provided, but it does not exist in the context, returns an empty list.
133
132
  :return: The records from the context that match the input parameters.
134
133
  """
135
- data_type: str = AliasUtil.to_data_type_name(data_type)
136
134
  records: dict[str, set[DataRecord]] = self.__base_id_to_records.get(record_id, {}) if record_id else self.__records
137
135
  return list(records.get(data_type, []))
138
136
 
139
137
  # FR-46701: Add functions to the rule handlers for accessing the field maps of inaccessible records in the context.
140
- def get_field_maps(self, data_type: DataTypeIdentifier, record_id: int | None = None) -> list[FieldMap]:
138
+ def get_field_maps(self, data_type: str, record_id: int | None = None) -> list[FieldMap]:
141
139
  """
142
140
  Get field maps from the cached context with the given data type. Capable of being filtered to searching within
143
141
  the context of a record ID. If the given data type or record ID does not exist in the context,
@@ -153,7 +151,6 @@ class OnSaveRuleHandler:
153
151
  list.
154
152
  :return: The field maps from the context that match the input parameters.
155
153
  """
156
- data_type: str = AliasUtil.to_data_type_name(data_type)
157
154
  field_maps: dict[str, dict[int, FieldMap]] = self.__base_id_to_field_maps.get(record_id, {}) if record_id else self.__field_maps
158
155
  return list(field_maps.get(data_type, {}).values())
159
156
 
@@ -4,25 +4,16 @@ from logging import Logger
4
4
 
5
5
  from sapiopylib.rest.DataMgmtService import DataMgmtServer
6
6
  from sapiopylib.rest.DataRecordManagerService import DataRecordManager
7
- from sapiopylib.rest.User import SapioUser
8
7
  from sapiopylib.rest.WebhookService import AbstractWebhookHandler
9
- from sapiopylib.rest.pojo.Message import VeloxLogMessage, VeloxLogLevel
10
8
  from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
11
9
  from sapiopylib.rest.pojo.webhook.WebhookEnums import WebhookEndpointType
12
10
  from sapiopylib.rest.pojo.webhook.WebhookResult import SapioWebhookResult
13
- from sapiopylib.rest.utils.DataTypeCacheManager import DataTypeCacheManager
14
11
  from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelManager, RecordModelInstanceManager, \
15
12
  RecordModelRelationshipManager
16
13
  from sapiopylib.rest.utils.recordmodel.ancestry import RecordModelAncestorManager
17
14
 
18
- from sapiopycommons.callbacks.callback_util import CallbackUtil
19
- from sapiopycommons.eln.experiment_handler import ExperimentHandler
20
15
  from sapiopycommons.general.exceptions import SapioUserErrorException, SapioCriticalErrorException, \
21
- SapioUserCancelledException, SapioException
22
- from sapiopycommons.general.sapio_links import SapioNavigationLinker
23
- from sapiopycommons.recordmodel.record_handler import RecordHandler
24
- from sapiopycommons.rules.eln_rule_handler import ElnRuleHandler
25
- from sapiopycommons.rules.on_save_rule_handler import OnSaveRuleHandler
16
+ SapioUserCancelledException
26
17
 
27
18
 
28
19
  # FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
@@ -34,7 +25,6 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
34
25
  """
35
26
  logger: Logger
36
27
 
37
- user: SapioUser
38
28
  context: SapioWebhookContext
39
29
 
40
30
  dr_man: DataRecordManager
@@ -44,46 +34,21 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
44
34
  # FR-46329: Add the ancestor manager to CommonsWebhookHandler.
45
35
  an_man: RecordModelAncestorManager
46
36
 
47
- dt_cache: DataTypeCacheManager
48
- rec_handler: RecordHandler
49
- callback: CallbackUtil
50
- exp_handler: ExperimentHandler | None
51
- rule_handler: OnSaveRuleHandler | ElnRuleHandler | None
52
-
53
37
  def run(self, context: SapioWebhookContext) -> SapioWebhookResult:
54
- self.user = context.user
55
38
  self.context = context
56
-
57
- self.logger = self.user.logger
39
+ self.logger = context.user.logger
58
40
 
59
41
  self.dr_man = context.data_record_manager
60
- self.rec_man = RecordModelManager(self.user)
42
+ self.rec_man = RecordModelManager(context.user)
61
43
  self.inst_man = self.rec_man.instance_manager
62
44
  self.rel_man = self.rec_man.relationship_manager
63
45
  self.an_man = RecordModelAncestorManager(self.rec_man)
64
46
 
65
- self.dt_cache = DataTypeCacheManager(self.user)
66
- self.rec_handler = RecordHandler(context)
67
- self.callback = CallbackUtil(context)
68
- if context.eln_experiment is not None:
69
- self.exp_handler = ExperimentHandler(context)
70
- else:
71
- self.exp_handler = None
72
- if self.is_on_save_rule():
73
- self.rule_handler = OnSaveRuleHandler(context)
74
- elif self.is_eln_rule():
75
- self.rule_handler = ElnRuleHandler(context)
76
- else:
77
- self.rule_handler = None
78
-
79
47
  # Wrap the execution of each webhook in a try/catch. If an exception occurs, handle any special sapiopycommons
80
48
  # exceptions. Otherwise, return a generic message stating that an error occurred.
81
49
  try:
82
50
  self.initialize(context)
83
- result = self.execute(context)
84
- if result is None:
85
- raise SapioException("Your execute function returned a None result! Don't forget your return statement!")
86
- return result
51
+ return self.execute(context)
87
52
  except SapioUserErrorException as e:
88
53
  return self.handle_user_error_exception(e)
89
54
  except SapioCriticalErrorException as e:
@@ -135,7 +100,7 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
135
100
  return result
136
101
  self.log_error(traceback.format_exc())
137
102
  if self.can_send_client_callback():
138
- DataMgmtServer.get_client_callback(self.user).display_error(e.args[0])
103
+ DataMgmtServer.get_client_callback(self.context.user).display_error(e.args[0])
139
104
  return SapioWebhookResult(False)
140
105
 
141
106
  def handle_unexpected_exception(self, e: Exception) -> SapioWebhookResult:
@@ -149,10 +114,7 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
149
114
  result: SapioWebhookResult | None = self.handle_any_exception(e)
150
115
  if result is not None:
151
116
  return result
152
- msg: str = traceback.format_exc()
153
- self.log_error(msg)
154
- # FR-47079: Also log all unexpected exception messages to the webhook execution log within the platform.
155
- self.log_error_to_webhook_execution_log(msg)
117
+ self.log_error(traceback.format_exc())
156
118
  return SapioWebhookResult(False, display_text="Unexpected error occurred during webhook execution. "
157
119
  "Please contact Sapio support.")
158
120
 
@@ -167,7 +129,7 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
167
129
  result: SapioWebhookResult | None = self.handle_any_exception(e)
168
130
  if result is not None:
169
131
  return result
170
- return SapioWebhookResult(False)
132
+ return SapioWebhookResult(False, display_text="User cancelled.")
171
133
 
172
134
  # noinspection PyMethodMayBeStatic,PyUnusedLocal
173
135
  def handle_any_exception(self, e: Exception) -> SapioWebhookResult | None:
@@ -183,58 +145,32 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
183
145
 
184
146
  def log_info(self, msg: str) -> None:
185
147
  """
186
- Write an info message to the webhook server log. Log destination is stdout. This message will be prepended with
187
- the user's username and the experiment ID of the experiment they are in, if any.
148
+ Write an info message to the log. Log destination is stdout. This message will be prepended with the user's
149
+ username and the experiment ID of the experiment they are in, if any.
188
150
  """
189
- self.logger.info(self._format_log(msg, "log_info call"))
151
+ exp_id = None
152
+ if self.context.eln_experiment is not None:
153
+ exp_id = self.context.eln_experiment.notebook_experiment_id
154
+ # CR-46333: Add the user's group to the logging message.
155
+ user = self.context.user
156
+ username = user.username
157
+ group_name = user.session_additional_data.current_group_name
158
+ self.logger.info(f"(User: {username}, Group: {group_name}, Experiment: {exp_id}):\n{msg}")
190
159
 
191
160
  def log_error(self, msg: str) -> None:
192
161
  """
193
- Write an error message to the webhook server log. Log destination is stderr. This message will be prepended with
194
- the user's username and the experiment ID of the experiment they are in, if any.
162
+ Write an error message to the log. Log destination is stderr. This message will be prepended with the user's
163
+ username and the experiment ID of the experiment they are in, if any.
195
164
  """
196
- # PR-46209: Use logger.error instead of logger.info when logging errors.
197
- self.logger.error(self._format_log(msg, "log_error call"))
198
-
199
- def log_error_to_webhook_execution_log(self, msg: str) -> None:
200
- """
201
- Write an error message to the platform's webhook execution log. This can be reviewed by navigating to the
202
- webhook configuration where the webhook that called this function is defined and clicking the "View Log"
203
- button. From there, select one of the rows for the webhook executions and click "Download Log" from the right
204
- side table.
205
- """
206
- messenger = DataMgmtServer.get_messenger(self.user)
207
- messenger.log_message(VeloxLogMessage(message=self._format_log(msg, "Error occurred during webhook execution."),
208
- log_level=VeloxLogLevel.ERROR,
209
- originating_class=self.__class__.__name__))
210
-
211
- def _format_log(self, msg: str, prefix: str | None = None) -> str:
212
- """
213
- Given a message to log, populate it with some metadata about this particular webhook execution, including
214
- the group of the user and the invocation type of the webhook call.
215
- """
216
- # If we're able to, provide a link to the location that the error occurred at.
217
- navigator = SapioNavigationLinker(self.context)
165
+ exp_id = None
218
166
  if self.context.eln_experiment is not None:
219
- link = navigator.experiment(self.context.eln_experiment)
220
- elif self.context.data_record and not self.context.data_record_list:
221
- link = navigator.data_record(self.context.data_record)
222
- elif self.context.base_data_record:
223
- link = navigator.data_record(self.context.base_data_record)
224
- else:
225
- link = None
226
-
227
- message: str = ""
228
- if prefix:
229
- message += prefix + "\n"
230
- message += f"Webhook invocation type: {self.context.end_point_type.display_name}\n"
231
- message += f"Username: {self.user.username}\n"
167
+ exp_id = self.context.eln_experiment.notebook_experiment_id
232
168
  # CR-46333: Add the user's group to the logging message.
233
- message += f"User group: {self.user.session_additional_data.current_group_name}\n"
234
- if link:
235
- message += f"User location: {link}\n"
236
- message += msg
237
- return message
169
+ user = self.context.user
170
+ username = user.username
171
+ group_name = user.session_additional_data.current_group_name
172
+ # PR-46209: Use logger.error instead of logger.info when logging errors.
173
+ self.logger.error(f"(User: {username}, Group: {group_name}, Experiment: {exp_id}):\n{msg}")
238
174
 
239
175
  def is_main_toolbar(self) -> bool:
240
176
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2024.8.28a315
3
+ Version: 2024.8.29a317
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>
@@ -0,0 +1,43 @@
1
+ sapiopycommons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ sapiopycommons/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ sapiopycommons/callbacks/callback_util.py,sha256=caeIWCHvK33jDs3TRskpJv0kDe7W8NPK4MyJPjgztwo,58012
4
+ sapiopycommons/chem/IndigoMolecules.py,sha256=QqFDi9CKERj6sn_ZwVcS2xZq4imlkaTeCrpq1iNcEJA,1992
5
+ sapiopycommons/chem/Molecules.py,sha256=t80IsQBPJ9mwE8ZxnWomAGrZDhdsOuPvLaTPb_N6jGU,8639
6
+ sapiopycommons/chem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ sapiopycommons/datatype/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ sapiopycommons/datatype/attachment_util.py,sha256=YlnMprj5IGBbAZDLG2khS1P7JIYTw_NYfpJAfRZfP3M,3219
9
+ sapiopycommons/eln/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ sapiopycommons/eln/experiment_handler.py,sha256=v1pG4qtZb8OSNWfKtFo6NjnEkReqnu5R9i_hqWh_xxg,57198
11
+ sapiopycommons/eln/experiment_report_util.py,sha256=FTLw-6SLAMeoWTOO-qhGROE9g54pZdyoQJIhiIzlwGw,7848
12
+ sapiopycommons/eln/plate_designer.py,sha256=FYJfhhNq8hdfuXgDYOYHy6g0m2zNwQXZWF_MTPzElDg,7184
13
+ sapiopycommons/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ sapiopycommons/files/complex_data_loader.py,sha256=XSJOl676mIklJo78v07-70u1b015a5DI4sqZPI3C-Tw,1475
15
+ sapiopycommons/files/file_bridge.py,sha256=GI3-gWFzcL0q0c8jKOxTevbzJqtUpiElmkXfTnMsaOo,6224
16
+ sapiopycommons/files/file_bridge_handler.py,sha256=MU2wZR4VY606yx6Bnv8-LzG3mGCeuXeRBn914WNRFCo,13601
17
+ sapiopycommons/files/file_data_handler.py,sha256=3-guAdhJdeJWAFq1a27ijspkO7uMMZ6CapMCD_6o4jA,36746
18
+ sapiopycommons/files/file_util.py,sha256=44mzhn3M_QltoncBB-ooX7_yO6u5k-XU_bzUXHGxUiw,26299
19
+ sapiopycommons/files/file_validator.py,sha256=BhXB2XnoNEzdBXuwul1s2RNoj-3ZoiMmephUCU_0o3Y,28113
20
+ sapiopycommons/files/file_writer.py,sha256=5u_iZXTQvuUU7ceHZr8Q001_tvgJhOqBwAnB_pxcAbQ,16027
21
+ sapiopycommons/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ sapiopycommons/general/accession_service.py,sha256=HYgyOsH_UaoRnoury-c2yTW8SeG4OtjLemdpCzoV4R8,13484
23
+ sapiopycommons/general/aliases.py,sha256=i6af5o2oVFGNcyk7GkvTWXQs0H9xTbFKc_GIah8NKVU,3594
24
+ sapiopycommons/general/custom_report_util.py,sha256=cLgIR5Fn3M9uyAtgfTYRv3JRk2SKNevnsb_R5zidSYs,15557
25
+ sapiopycommons/general/exceptions.py,sha256=DOlLKnpCatxQF-lVCToa8ryJgusWLvip6N_1ALN00QE,1679
26
+ sapiopycommons/general/popup_util.py,sha256=-mN5IgYPrLrOEHJ4CHPi2rec4_WAN6X0yMxHwD5h3Bs,30126
27
+ sapiopycommons/general/storage_util.py,sha256=ovmK_jN7v09BoX07XxwShpBUC5WYQOM7dbKV_VeLXJU,8892
28
+ sapiopycommons/general/time_util.py,sha256=jiJUh7jc1ZRCOem880S3HaLPZ4RboBtSl4_U9sqAQuM,7290
29
+ sapiopycommons/multimodal/multimodal.py,sha256=A1QsC8QTPmgZyPr7KtMbPRedn2Ie4WIErodUvQ9otgU,6724
30
+ sapiopycommons/multimodal/multimodal_data.py,sha256=zqgYHO-ULaPKV0POFWZVY9N-Sfm1RQWwdsfwFxe5DjI,15038
31
+ sapiopycommons/processtracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ sapiopycommons/processtracking/endpoints.py,sha256=g5h_uCVByqacYm9zWAz8TyAdRsGfaO2o0b5RSJdOaSA,10926
33
+ sapiopycommons/recordmodel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ sapiopycommons/recordmodel/record_handler.py,sha256=AyK1H3x-g1eu1Mt9XD1h57yRrZp_TJjZlEaQ2kPP4Dc,54432
35
+ sapiopycommons/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ sapiopycommons/rules/eln_rule_handler.py,sha256=qfkBZtck0KK1i9s9Xe2UZqkzQOgPCzDxRkhxE8Si1xk,10671
37
+ sapiopycommons/rules/on_save_rule_handler.py,sha256=JY9F30IcHwFVdgPAMQtTYuRastV1jeezhVktyrzNASU,10763
38
+ sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ sapiopycommons/webhook/webhook_handlers.py,sha256=ibpBY3Sk3Eij919bIdW0awzlogYoQSWYDDOg--NwsQE,13431
40
+ sapiopycommons-2024.8.29a317.dist-info/METADATA,sha256=Pgj3R0rfc31rPqz3voTIjd6r1axXbXfCSfwXI3eCZGQ,3176
41
+ sapiopycommons-2024.8.29a317.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
42
+ sapiopycommons-2024.8.29a317.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
43
+ sapiopycommons-2024.8.29a317.dist-info/RECORD,,
File without changes
@@ -1,60 +0,0 @@
1
- from sapiopylib.rest.pojo.CustomReport import ReportColumn
2
- from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
3
-
4
- from sapiopycommons.general.aliases import DataTypeIdentifier, FieldIdentifier, AliasUtil
5
- from sapiopycommons.general.exceptions import SapioException
6
-
7
- # The system fields that every record has and their field types. System fields aren't generated as record model fields
8
- # for all platform version, hence the need to create a dict for them in the off chance that they're not present on
9
- # the model wrapper.
10
- SYSTEM_FIELDS: dict[str, FieldType] = {
11
- "DataRecordName": FieldType.IDENTIFIER,
12
- "RecordId": FieldType.LONG,
13
- "DateCreated": FieldType.DATE,
14
- "CreatedBy": FieldType.STRING,
15
- "VeloxLastModifiedDate": FieldType.DATE,
16
- "VeloxLastModifiedBy": FieldType.STRING
17
- }
18
-
19
-
20
- class ColumnBuilder:
21
- """
22
- A class for building report columns for custom reports.
23
- """
24
- @staticmethod
25
- def build_column(data_type: DataTypeIdentifier, field: FieldIdentifier, field_type: FieldType | None = None) \
26
- -> ReportColumn:
27
- """
28
- Build a ReportColumn from a variety of possible inputs.
29
-
30
- :param data_type: An object that can be used to identify a data type.
31
- :param field: An object that can be used to identify a data field.
32
- :param field_type: The field type of the provided field. This is only required if the field type cannot be
33
- determined from the given data type and field, which occurs when the given field is a string and the
34
- given data type is not a wrapped record model or record model wrapper.
35
- :return: A ReportColumn for the inputs.
36
- """
37
- # Get the data type and field names from the inputs.
38
- data_type_name = AliasUtil.to_data_type_name(data_type)
39
- field_name = AliasUtil.to_data_field_name(field)
40
- if field_type is None:
41
- field_type = ColumnBuilder.__field_type(data_type, field)
42
- if field_type is None:
43
- raise SapioException("The field_type parameter is required for the provided data_type and field inputs.")
44
- return ReportColumn(data_type_name, field_name, field_type)
45
-
46
- @staticmethod
47
- def __field_type(data_type: DataTypeIdentifier, field: FieldIdentifier) -> FieldType | None:
48
- """
49
- Given a record model wrapper and a field name, return the field type for that field. Accounts for system fields.
50
-
51
- :param data_type: The record model wrapper that the field is on.
52
- :param field: The field name to return the type of.
53
- :return: The field type of the given field name.
54
- """
55
- # Check if the field name is a system field. If it us, use the field type defined in this file.
56
- field_name: str = AliasUtil.to_data_field_name(field)
57
- if field_name in SYSTEM_FIELDS:
58
- return SYSTEM_FIELDS.get(field_name)
59
- # Otherwise, check if the field type can be found from the wrapper.
60
- return AliasUtil.to_field_type(field_name, data_type)