sapiopycommons 2024.8.29a317__py3-none-any.whl → 2024.8.30a320__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.

Files changed (33) hide show
  1. sapiopycommons/callbacks/callback_util.py +133 -37
  2. sapiopycommons/customreport/__init__.py +0 -0
  3. sapiopycommons/customreport/column_builder.py +60 -0
  4. sapiopycommons/customreport/custom_report_builder.py +125 -0
  5. sapiopycommons/customreport/term_builder.py +299 -0
  6. sapiopycommons/datatype/attachment_util.py +11 -10
  7. sapiopycommons/eln/experiment_handler.py +209 -48
  8. sapiopycommons/eln/experiment_report_util.py +33 -129
  9. sapiopycommons/files/complex_data_loader.py +5 -4
  10. sapiopycommons/files/file_bridge.py +15 -14
  11. sapiopycommons/files/file_bridge_handler.py +27 -5
  12. sapiopycommons/files/file_data_handler.py +2 -5
  13. sapiopycommons/files/file_util.py +38 -5
  14. sapiopycommons/files/file_validator.py +26 -11
  15. sapiopycommons/files/file_writer.py +44 -15
  16. sapiopycommons/general/aliases.py +147 -3
  17. sapiopycommons/general/audit_log.py +196 -0
  18. sapiopycommons/general/custom_report_util.py +34 -32
  19. sapiopycommons/general/popup_util.py +17 -0
  20. sapiopycommons/general/sapio_links.py +50 -0
  21. sapiopycommons/general/time_util.py +40 -0
  22. sapiopycommons/multimodal/multimodal_data.py +0 -1
  23. sapiopycommons/processtracking/endpoints.py +22 -22
  24. sapiopycommons/recordmodel/record_handler.py +228 -77
  25. sapiopycommons/rules/eln_rule_handler.py +34 -25
  26. sapiopycommons/rules/on_save_rule_handler.py +34 -31
  27. sapiopycommons/webhook/webhook_handlers.py +90 -26
  28. sapiopycommons/webhook/webservice_handlers.py +67 -0
  29. {sapiopycommons-2024.8.29a317.dist-info → sapiopycommons-2024.8.30a320.dist-info}/METADATA +1 -1
  30. sapiopycommons-2024.8.30a320.dist-info/RECORD +50 -0
  31. sapiopycommons-2024.8.29a317.dist-info/RECORD +0 -43
  32. {sapiopycommons-2024.8.29a317.dist-info → sapiopycommons-2024.8.30a320.dist-info}/WHEEL +0 -0
  33. {sapiopycommons-2024.8.29a317.dist-info → sapiopycommons-2024.8.30a320.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,5 @@
1
+ import warnings
2
+
1
3
  from sapiopylib.rest.DataMgmtService import DataMgmtServer
2
4
  from sapiopylib.rest.pojo.datatype.DataType import DataTypeDefinition
3
5
  from sapiopylib.rest.pojo.datatype.FieldDefinition import VeloxStringFieldDefinition, AbstractVeloxFieldDefinition, \
@@ -51,6 +53,7 @@ class PopupUtil:
51
53
  :param request_context: Context that will be returned to the webhook server in the client callback result.
52
54
  :return: A SapioWebhookResult with the popup as its client callback request.
53
55
  """
56
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
54
57
  if display_name is None:
55
58
  display_name = data_type
56
59
  if plural_display_name is None:
@@ -97,6 +100,7 @@ class PopupUtil:
97
100
  :param request_context: Context that will be returned to the webhook server in the client callback result.
98
101
  :return: A SapioWebhookResult with the popup as its client callback request.
99
102
  """
103
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
100
104
  # Get the field definitions of the data type.
101
105
  data_type: str = record.data_type_name
102
106
  type_man = DataMgmtServer.get_data_type_manager(context.user)
@@ -155,6 +159,7 @@ class PopupUtil:
155
159
  :param request_context: Context that will be returned to the webhook server in the client callback result.
156
160
  :return: A SapioWebhookResult with the popup as its client callback request.
157
161
  """
162
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
158
163
  if max_length is None:
159
164
  max_length = len(default_value) if default_value else 100
160
165
  string_field = VeloxStringFieldDefinition(data_type, field_name, field_name, default_value=default_value,
@@ -191,6 +196,7 @@ class PopupUtil:
191
196
  :param request_context: Context that will be returned to the webhook server in the client callback result.
192
197
  :return: A SapioWebhookResult with the popup as its client callback request.
193
198
  """
199
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
194
200
  if default_value is None:
195
201
  default_value = max(0, min_value)
196
202
  integer_field = VeloxIntegerFieldDefinition(data_type, field_name, field_name, default_value=default_value,
@@ -229,6 +235,7 @@ class PopupUtil:
229
235
  :param request_context: Context that will be returned to the webhook server in the client callback result.
230
236
  :return: A SapioWebhookResult with the popup as its client callback request.
231
237
  """
238
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
232
239
  if default_value is None:
233
240
  default_value = min_value
234
241
  double_field = VeloxDoubleFieldDefinition(data_type, field_name, field_name, default_value=default_value,
@@ -260,6 +267,7 @@ class PopupUtil:
260
267
  :param request_context: Context that will be returned to the webhook server in the client callback result.
261
268
  :return: A SapioWebhookResult with the popup as its client callback request.
262
269
  """
270
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
263
271
  if display_name is None:
264
272
  display_name = data_type
265
273
  if plural_display_name is None:
@@ -295,6 +303,7 @@ class PopupUtil:
295
303
  :param request_context: Context that will be returned to the webhook server in the client callback result.
296
304
  :return: A SapioWebhookResult with the popup as its client callback request.
297
305
  """
306
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
298
307
  data_types: set[str] = {x.data_type_name for x in records}
299
308
  if len(data_types) > 1:
300
309
  raise SapioException("Multiple data type names encountered in records list for record table popup.")
@@ -347,6 +356,7 @@ class PopupUtil:
347
356
  :param request_context: Context that will be returned to the webhook server in the client callback result.
348
357
  :return: A SapioWebhookResult with the popup as its client callback request.
349
358
  """
359
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
350
360
  data_types: set[str] = {x.data_type_name for x in records}
351
361
  if len(data_types) > 1:
352
362
  raise SapioException("Multiple data type names encountered in records list for record table popup.")
@@ -391,6 +401,7 @@ class PopupUtil:
391
401
  :param request_context: Context that will be returned to the webhook server in the client callback result.
392
402
  :return: A SapioWebhookResult with the popup as its client callback request.
393
403
  """
404
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
394
405
  callback = ListDialogRequest(title, multi_select, options,
395
406
  callback_context_data=request_context)
396
407
  return SapioWebhookResult(True, client_callback_request=callback)
@@ -415,6 +426,7 @@ class PopupUtil:
415
426
  :param request_context: Context that will be returned to the webhook server in the client callback result.
416
427
  :return: A SapioWebhookResult with the popup as its client callback request.
417
428
  """
429
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
418
430
  callback = OptionDialogRequest(title, msg, options, default_option, user_can_cancel,
419
431
  callback_context_data=request_context)
420
432
  return SapioWebhookResult(True, client_callback_request=callback)
@@ -437,6 +449,7 @@ class PopupUtil:
437
449
  :param request_context: Context that will be returned to the webhook server in the client callback result.
438
450
  :return: A SapioWebhookResult with the popup as its client callback request.
439
451
  """
452
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
440
453
  return PopupUtil.option_popup(title, msg, ["OK"], 0, user_can_cancel, request_context=request_context)
441
454
 
442
455
  @staticmethod
@@ -458,6 +471,7 @@ class PopupUtil:
458
471
  :param request_context: Context that will be returned to the webhook server in the client callback result.
459
472
  :return: A SapioWebhookResult with the popup as its client callback request.
460
473
  """
474
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
461
475
  return PopupUtil.option_popup(title, msg, ["Yes", "No"], 0 if default_yes else 1, user_can_cancel,
462
476
  request_context=request_context)
463
477
 
@@ -470,6 +484,7 @@ class PopupUtil:
470
484
 
471
485
  Deprecated for PopupUtil.text_field_popup.
472
486
  """
487
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
473
488
  return PopupUtil.string_field_popup(title, "", field_name, msg, len(msg), False, data_type,
474
489
  request_context=request_context, auto_size=True)
475
490
 
@@ -481,6 +496,7 @@ class PopupUtil:
481
496
 
482
497
  Deprecated for PopupUtil.option_popup.
483
498
  """
499
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
484
500
  return PopupUtil.option_popup(title, msg, options, 0, user_can_cancel, request_context=request_context)
485
501
 
486
502
  @staticmethod
@@ -490,4 +506,5 @@ class PopupUtil:
490
506
 
491
507
  Deprecated for PopupUtil.ok_popup.
492
508
  """
509
+ warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
493
510
  return PopupUtil.ok_popup(title, msg, False, request_context=request_context)
@@ -0,0 +1,50 @@
1
+ from sapiopylib.rest.User import SapioUser
2
+ from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
3
+
4
+ from sapiopycommons.general.aliases import RecordIdentifier, ExperimentIdentifier, AliasUtil, DataTypeIdentifier
5
+ from sapiopycommons.general.exceptions import SapioException
6
+
7
+
8
+ class SapioNavigationLinker:
9
+ """
10
+ Given a URL to a system's webservice API (example: https://company.exemplareln.com/webservice/api), construct
11
+ URLs for navigation links to various locations in the system.
12
+ """
13
+ base_url: str
14
+
15
+ def __init__(self, url: str | SapioUser | SapioWebhookContext):
16
+ """
17
+ :param url: A user or context object that is being used to send requests to a Sapio system, or a URL to a
18
+ system's webservice API.
19
+ """
20
+ if isinstance(url, SapioWebhookContext):
21
+ url = url.user.url
22
+ elif isinstance(url, SapioUser):
23
+ url = url.url
24
+ self.base_url = url.rstrip("/").replace('webservice/api', 'veloxClient')
25
+
26
+ def data_record(self, record_identifier: RecordIdentifier, data_type_name: DataTypeIdentifier | None = None) -> str:
27
+ """
28
+ :param record_identifier: An object that can be used to identify a record in the system, be that a record ID,
29
+ a data record, or a record model.
30
+ :param data_type_name: If the provided record identifier is a record ID, then the data type name of the record
31
+ must be provided in this parameter. Otherwise, this parameter is ignored.
32
+ :return: A URL for navigating to the input record.
33
+ """
34
+ record_id: int = AliasUtil.to_record_id(record_identifier)
35
+ if data_type_name:
36
+ data_type_name = AliasUtil.to_data_type_name(data_type_name)
37
+ if not isinstance(record_identifier, int):
38
+ data_type_name = AliasUtil.to_data_type_name(record_identifier)
39
+ if not data_type_name:
40
+ raise SapioException("Unable to create a data record link without a data type name. "
41
+ "Only a record ID was provided.")
42
+ return self.base_url + f"/#dataType={data_type_name};recordId={record_id};view=dataRecord"
43
+
44
+ def experiment(self, experiment: ExperimentIdentifier) -> str:
45
+ """
46
+ :param experiment: An object that can be used to identify an experiment in the system, be that an experiment
47
+ object, experiment protocol, or a notebook ID.
48
+ :return: A URL for navigating to the input experiment.
49
+ """
50
+ return self.base_url + f"/#notebookExperimentId={AliasUtil.to_notebook_id(experiment)};view=eln"
@@ -1,8 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import time
2
4
  from datetime import datetime
3
5
 
4
6
  import pytz
5
7
 
8
+ from sapiopycommons.general.exceptions import SapioException
9
+
6
10
  __timezone = None
7
11
  """The default timezone. Use TimeUtil.set_default_timezone in a global context before making use of TimeUtil."""
8
12
 
@@ -137,3 +141,39 @@ class TimeUtil:
137
141
  return True
138
142
  except Exception:
139
143
  return False
144
+
145
+
146
+ class DateRange:
147
+ start: int | None
148
+ end: int | None
149
+
150
+ @staticmethod
151
+ def from_string(value: str | None) -> DateRange:
152
+ """
153
+ Construct a DateRange object from a string. The field value of date range fields is a string of the form
154
+ <start timestamp>/<end timestamp>.
155
+
156
+ :param value: A date range field value.
157
+ :return: A DateRange object matching the input field value.
158
+ """
159
+ if not value:
160
+ return DateRange(None, None)
161
+ values: list[str] = value.split("/")
162
+ return DateRange(int(values[0]), int(values[1]))
163
+
164
+ def __init__(self, start: int | None, end: int | None):
165
+ """
166
+ :param start: The timestamp for the start of the date range.
167
+ :param end: The timestamp for the end of the date rate.
168
+ """
169
+ if (start and end is None) or (end and start is None):
170
+ raise SapioException("Both start and end values must be present in a date range.")
171
+ if start and end and end < start:
172
+ raise SapioException(f"End timestamp {end} is earlier than the start timestamp {start}.")
173
+ self.start = start
174
+ self.end = end
175
+
176
+ def __str__(self) -> str | None:
177
+ if not self.start and not self.end:
178
+ return None
179
+ return f"{self.start}/{self.end}"
@@ -6,7 +6,6 @@ from typing import Any
6
6
 
7
7
  from databind.core import ExtraKeys
8
8
  from databind.core.dataclasses import dataclass
9
- from sapiopylib.rest.pojo.DataRecord import DataRecord
10
9
 
11
10
 
12
11
  @dataclass
@@ -1,12 +1,12 @@
1
1
  from sapiopylib.rest.User import SapioUser
2
- from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
3
2
 
4
- from sapiopycommons.general.aliases import RecordIdentifier, AliasUtil, ExperimentIdentifier
3
+ from sapiopycommons.general.aliases import RecordIdentifier, AliasUtil, ExperimentIdentifier, DataTypeIdentifier, \
4
+ UserIdentifier
5
5
 
6
6
 
7
7
  class ProcessTracking:
8
8
  @staticmethod
9
- def assign_to_process(context: SapioWebhookContext | SapioUser, data_type: str, records: list[RecordIdentifier],
9
+ def assign_to_process(context: UserIdentifier, data_type: DataTypeIdentifier, records: list[RecordIdentifier],
10
10
  process_name: str, step_number: int | None = None, branch_id: int | None = None,
11
11
  request: RecordIdentifier | None = None) -> None:
12
12
  """
@@ -27,19 +27,19 @@ class ProcessTracking:
27
27
  """
28
28
  sub_path = '/ext/process-tracking/assign-to-process'
29
29
  payload = {
30
- "data-type-name": data_type,
30
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
31
31
  "record-ids": AliasUtil.to_record_ids(records),
32
32
  "process-name": process_name,
33
33
  "step-number": step_number,
34
34
  "branch-id": branch_id,
35
35
  "request-record-id": AliasUtil.to_record_ids([request])[0] if request is not None else None
36
36
  }
37
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
37
+ user: SapioUser = AliasUtil.to_sapio_user(context)
38
38
  response = user.post(sub_path, payload=payload)
39
39
  user.raise_for_status(response)
40
40
 
41
41
  @staticmethod
42
- def begin_protocol(context: SapioWebhookContext | SapioUser, data_type: str, records: list[RecordIdentifier],
42
+ def begin_protocol(context: UserIdentifier, data_type: DataTypeIdentifier, records: list[RecordIdentifier],
43
43
  experiment: ExperimentIdentifier) -> None:
44
44
  """
45
45
  Begin the assigned processes of the given tracked records as the given experiment. This sets the status of the
@@ -54,16 +54,16 @@ class ProcessTracking:
54
54
  """
55
55
  sub_path = '/ext/process-tracking/begin-protocol'
56
56
  payload = {
57
- "data-type-name": data_type,
57
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
58
58
  "record-ids": AliasUtil.to_record_ids(records),
59
59
  "experiment-id": AliasUtil.to_notebook_id(experiment),
60
60
  }
61
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
61
+ user: SapioUser = AliasUtil.to_sapio_user(context)
62
62
  response = user.post(sub_path, payload=payload)
63
63
  user.raise_for_status(response)
64
64
 
65
65
  @staticmethod
66
- def complete_protocol(context: SapioWebhookContext | SapioUser, data_type: str, records: list[RecordIdentifier],
66
+ def complete_protocol(context: UserIdentifier, data_type: DataTypeIdentifier, records: list[RecordIdentifier],
67
67
  experiment: ExperimentIdentifier) -> None:
68
68
  """
69
69
  Complete the current step that the given tracked records are at given the experiment.
@@ -80,16 +80,16 @@ class ProcessTracking:
80
80
  """
81
81
  sub_path = '/ext/process-tracking/complete-protocol'
82
82
  payload = {
83
- "data-type-name": data_type,
83
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
84
84
  "record-ids": AliasUtil.to_record_ids(records),
85
85
  "experiment-id": AliasUtil.to_notebook_id(experiment),
86
86
  }
87
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
87
+ user: SapioUser = AliasUtil.to_sapio_user(context)
88
88
  response = user.post(sub_path, payload=payload)
89
89
  user.raise_for_status(response)
90
90
 
91
91
  @staticmethod
92
- def fail(context: SapioWebhookContext | SapioUser, data_type: str, records: list[RecordIdentifier],
92
+ def fail(context: UserIdentifier, data_type: DataTypeIdentifier, records: list[RecordIdentifier],
93
93
  experiment: ExperimentIdentifier) -> None:
94
94
  """
95
95
  Fail the assigned processes of the given tracked records, changing their statuses to "Failed -". The tracked
@@ -103,16 +103,16 @@ class ProcessTracking:
103
103
  """
104
104
  sub_path = '/ext/process-tracking/fail'
105
105
  payload = {
106
- "data-type-name": data_type,
106
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
107
107
  "record-ids": AliasUtil.to_record_ids(records),
108
108
  "experiment-id": AliasUtil.to_notebook_id(experiment),
109
109
  }
110
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
110
+ user: SapioUser = AliasUtil.to_sapio_user(context)
111
111
  response = user.post(sub_path, payload=payload)
112
112
  user.raise_for_status(response)
113
113
 
114
114
  @staticmethod
115
- def promote_to_next_by_experiment(context: SapioWebhookContext | SapioUser, data_type: str,
115
+ def promote_to_next_by_experiment(context: UserIdentifier, data_type: DataTypeIdentifier,
116
116
  records: list[RecordIdentifier], experiment: ExperimentIdentifier) -> None:
117
117
  """
118
118
  Promote the status of the given tracked records to the next status in their process using an experiment.
@@ -129,16 +129,16 @@ class ProcessTracking:
129
129
  """
130
130
  sub_path = '/ext/process-tracking/promote-status-to-next'
131
131
  payload = {
132
- "data-type-name": data_type,
132
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
133
133
  "record-ids": AliasUtil.to_record_ids(records),
134
134
  "experiment-id": AliasUtil.to_notebook_id(experiment),
135
135
  }
136
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
136
+ user: SapioUser = AliasUtil.to_sapio_user(context)
137
137
  response = user.post(sub_path, payload=payload)
138
138
  user.raise_for_status(response)
139
139
 
140
140
  @staticmethod
141
- def promote_to_next_by_step(context: SapioWebhookContext | SapioUser, data_type: str,
141
+ def promote_to_next_by_step(context: UserIdentifier, data_type: DataTypeIdentifier,
142
142
  records: list[RecordIdentifier], process_name: str, step_number: int,
143
143
  branch_id: int | None = None) -> None:
144
144
  """
@@ -159,7 +159,7 @@ class ProcessTracking:
159
159
  """
160
160
  sub_path = '/ext/process-tracking/promote-status-to-next'
161
161
  payload = {
162
- "data-type-name": data_type,
162
+ "data-type-name": AliasUtil.to_data_type_name(data_type),
163
163
  "record-ids": AliasUtil.to_record_ids(records),
164
164
  "current-process-status": {
165
165
  "process-name": process_name,
@@ -167,12 +167,12 @@ class ProcessTracking:
167
167
  "branch-id": branch_id
168
168
  }
169
169
  }
170
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
170
+ user: SapioUser = AliasUtil.to_sapio_user(context)
171
171
  response = user.post(sub_path, payload=payload)
172
172
  user.raise_for_status(response)
173
173
 
174
174
  @staticmethod
175
- def reprocess(context: SapioWebhookContext | SapioUser, records: list[RecordIdentifier]) -> None:
175
+ def reprocess(context: UserIdentifier, records: list[RecordIdentifier]) -> None:
176
176
  """
177
177
  Reprocess tracked records to a previous step in their process. Reprocessing is controlled by ReturnPoint records
178
178
  which are children of the AssignedProcess on the tracked records. Creates a new AssignedProcess record for the
@@ -187,6 +187,6 @@ class ProcessTracking:
187
187
  payload = {
188
188
  "record-ids": AliasUtil.to_record_ids(records)
189
189
  }
190
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
190
+ user: SapioUser = AliasUtil.to_sapio_user(context)
191
191
  response = user.post(sub_path, payload=payload)
192
192
  user.raise_for_status(response)