sapiopycommons 2024.8.27a312__py3-none-any.whl → 2024.8.28a313__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.

@@ -3,10 +3,8 @@ from typing import Any
3
3
 
4
4
  from sapiopylib.rest.DataRecordManagerService import DataRecordManager
5
5
  from sapiopylib.rest.User import SapioUser
6
- from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, RawReportTerm, ReportColumn
7
6
  from sapiopylib.rest.pojo.DataRecord import DataRecord
8
7
  from sapiopylib.rest.pojo.DataRecordPaging import DataRecordPojoPageCriteria
9
- from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
10
8
  from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
11
9
  from sapiopylib.rest.utils.autopaging import QueryDataRecordsAutoPager, QueryDataRecordByIdListAutoPager, \
12
10
  QueryAllRecordsOfTypeAutoPager
@@ -77,43 +75,6 @@ class RecordHandler:
77
75
  """
78
76
  return self.query_models_with_criteria(wrapper_type, field, value_list, None, page_limit)[0]
79
77
 
80
- def query_and_map_models(self, wrapper_type: type[WrappedType], field: str, value_list: Iterable[Any],
81
- page_limit: int | None = None, *, mapping_field: str | None = None) \
82
- -> dict[Any, list[WrappedType]]:
83
- """
84
- Shorthand for using query_models to search for records given values on a specific field and then using
85
- map_by_field to turn the returned list into a dictionary mapping field values to records.
86
-
87
- :param wrapper_type: The record model wrapper to use.
88
- :param field: The field to query and map on.
89
- :param value_list: The values of the field to query on.
90
- :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
91
- :param mapping_field: If provided, use this field to map against instead of the field that was queried on.
92
- :return: The record models for the queried records mapped by field values to the records with that value.
93
- """
94
- if mapping_field is None:
95
- mapping_field = field
96
- return self.map_by_field(self.query_models(wrapper_type, field, value_list, page_limit), mapping_field)
97
-
98
- def query_and_unique_map_models(self, wrapper_type: type[WrappedType], field: str, value_list: Iterable[Any],
99
- page_limit: int | None = None, *, mapping_field: str | None = None) \
100
- -> dict[Any, WrappedType]:
101
- """
102
- Shorthand for using query_models to search for records given values on a specific field and then using
103
- map_by_unique_field to turn the returned list into a dictionary mapping field values to records.
104
- If any two records share the same field value, throws an exception.
105
-
106
- :param wrapper_type: The record model wrapper to use.
107
- :param field: The field to query and map on.
108
- :param value_list: The values of the field to query on.
109
- :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
110
- :param mapping_field: If provided, use this field to map against instead of the field that was queried on.
111
- :return: The record models for the queried records mapped by field values to the record with that value.
112
- """
113
- if mapping_field is None:
114
- mapping_field = field
115
- return self.map_by_unique_field(self.query_models(wrapper_type, field, value_list, page_limit), mapping_field)
116
-
117
78
  def query_models_with_criteria(self, wrapper_type: type[WrappedType], field: str, value_list: Iterable[Any],
118
79
  paging_criteria: DataRecordPojoPageCriteria | None = None,
119
80
  page_limit: int | None = None) \
@@ -199,58 +160,24 @@ class RecordHandler:
199
160
  return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
200
161
 
201
162
  def query_models_by_report(self, wrapper_type: type[WrappedType],
202
- report_name: str | RawReportTerm | CustomReportCriteria,
163
+ report_name: str,
203
164
  filters: dict[str, Iterable[Any]] | None = None,
204
- page_limit: int | None = None,
205
- page_size: int | None = None,
206
- page_number: int | None = None) -> list[WrappedType]:
165
+ page_limit: int | None = None) -> list[WrappedType]:
207
166
  """
208
- Run a report and use the results of that report to query for and return the records in the report results.
209
- First runs the report, then runs a data record manager query on the results of the custom report.
210
-
211
- Will throw an exception if given the name of a system report that does not have a RecordId column.
212
- Quick and custom reports are guaranteed to have a record ID column.
167
+ Run a system report that contains a RecordId column and query for the records with those IDs.
168
+ First runs the custom report, then runs a data record manager query on the results of the custom report.
213
169
 
214
- Any given custom report criteria should only have columns from a single data type.
170
+ Will throw an exception if the given system report does not have a RecordId column.
215
171
 
216
172
  :param wrapper_type: The record model wrapper to use.
217
- :param report_name: The name of a system report, or a raw report term for a quick report, or custom report
218
- criteria for a custom report.
173
+ :param report_name: The name of the system report to run.
219
174
  :param filters: If provided, filter the results of the report using the given mapping of headers to values to
220
175
  filter on. This filtering is done before the records are queried.
221
176
  :param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
222
- :param page_size: The size of each page of results in the search. If None, the page size is set by the server.
223
- If the input report is a custom report criteria, uses the value from the criteria, unless this value is
224
- not None, in which case it overwrites the given report's value.
225
- :param page_number: The page number to start the search from, If None, starts on the first page.
226
- If the input report is a custom report criteria, uses the value from the criteria, unless this value is
227
- not None, in which case it overwrites the given report's value.
228
- :return: The record models for the queried records that matched the given report.
229
- """
230
- if isinstance(report_name, str):
231
- results: list[dict[str, Any]] = CustomReportUtil.run_system_report(self.user, report_name, filters,
232
- page_limit, page_size, page_number)
233
- elif isinstance(report_name, RawReportTerm):
234
- results: list[dict[str, Any]] = CustomReportUtil.run_quick_report(self.user, report_name, filters,
235
- page_limit, page_size, page_number)
236
- elif isinstance(report_name, CustomReportCriteria):
237
- dt: str = wrapper_type.get_wrapper_data_type_name()
238
- # Ensure that the root data type is the one we're looking for.
239
- report_name.root_data_type = dt
240
- # Raise an exception if any column in the report doesn't match the given data type.
241
- if any([x.data_type_name != dt for x in report_name.column_list]):
242
- raise SapioException("You may only query records from a report containing columns from that data type.")
243
- # Enforce that the given custom report has a record ID column.
244
- if not any([x.data_type_name == dt and x.data_field_name == "RecordId" for x in report_name.column_list]):
245
- report_name.column_list.append(ReportColumn(dt, "RecordId", FieldType.LONG))
246
- results: list[dict[str, Any]] = CustomReportUtil.run_custom_report(self.user, report_name, filters,
247
- page_limit, page_size, page_number)
248
- else:
249
- raise SapioException("Unrecognized report object.")
250
-
251
- # Using the bracket accessor because we want to throw an exception if RecordId doesn't exist in the report.
252
- # This should only possibly be the case with system reports, as quick reports will include the record ID and
253
- # we forced any given custom report to have a record ID column.
177
+ :return: The record models for the queried records.
178
+ """
179
+ results: list[dict[str, Any]] = CustomReportUtil.run_system_report(self.user, report_name, filters, page_limit)
180
+ # Using the bracket operators because we want to throw an exception if RecordId doesn't exist in the report.
254
181
  ids: list[int] = [row["RecordId"] for row in results]
255
182
  return self.query_models_by_id(wrapper_type, ids)
256
183
 
@@ -284,46 +211,10 @@ class RecordHandler:
284
211
  the fields in the fields list.
285
212
  """
286
213
  models: list[WrappedType] = self.add_models(wrapper_type, len(fields))
287
- for model, field_list in zip(models, fields):
288
- model.set_field_values(field_list)
214
+ for model, field in zip(models, fields):
215
+ model.set_field_values(field)
289
216
  return models
290
217
 
291
- def find_or_add_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: Any,
292
- secondary_identifiers: FieldMap | None = None) -> WrappedType:
293
- """
294
- Find a unique record that matches the given field values. If no such records exist, add a record model to the
295
- cache with the identifying fields set to the desired values. This record will be created in the system when
296
- you store and commit changes. If more than one record with the identifying values exists, throws an exception.
297
-
298
- The record is searched for using the primary identifier field name and value. If multiple records are returned
299
- by the query on this primary identifier, then the secondary identifiers are used to filter the results.
300
-
301
- Makes a webservice call to query for the existing record.
302
-
303
- :param wrapper_type: The record model wrapper to use.
304
- :param primary_identifier: The data field name of the field to search on.
305
- :param id_value: The value of the identifying field to search for.
306
- :param secondary_identifiers: Optional fields used to filter the records that are returned after searching on
307
- the primary identifier.
308
- :return: The record model with the identifying field value, either pulled from the system or newly created.
309
- """
310
- # PR-46335: Initialize the secondary identifiers parameter if None is provided to avoid an exception.
311
- # If no secondary identifiers were provided, use an empty dictionary.
312
- if secondary_identifiers is None:
313
- secondary_identifiers = {}
314
-
315
- unique_record: WrappedType | None = self.__find_model(wrapper_type, primary_identifier, id_value,
316
- secondary_identifiers)
317
- # If a unique record matched the identifiers, return it.
318
- if unique_record is not None:
319
- return unique_record
320
-
321
- # If none of the results matched the identifiers, create a new record with all identifiers set.
322
- # Put the primary identifier and value into the secondary identifiers list and use that as the fields map
323
- # for this new record.
324
- secondary_identifiers.update({primary_identifier: id_value})
325
- return self.add_models_with_data(wrapper_type, [secondary_identifiers])[0]
326
-
327
218
  def create_models(self, wrapper_type: type[WrappedType], num: int) -> list[WrappedType]:
328
219
  """
329
220
  Shorthand for creating new records via the data record manager and then returning them as wrapped
@@ -379,8 +270,24 @@ class RecordHandler:
379
270
  if secondary_identifiers is None:
380
271
  secondary_identifiers = {}
381
272
 
382
- unique_record: WrappedType | None = self.__find_model(wrapper_type, primary_identifier, id_value,
383
- secondary_identifiers)
273
+ # Query for all records that match the primary identifier.
274
+ results: list[WrappedType] = self.query_models(wrapper_type, primary_identifier, [id_value])
275
+
276
+ # Find the one record, if any, that matches the secondary identifiers.
277
+ unique_record: WrappedType | None = None
278
+ for result in results:
279
+ matches_all: bool = True
280
+ for field, value in secondary_identifiers.items():
281
+ if result.get_field_value(field) != value:
282
+ matches_all = False
283
+ break
284
+ if matches_all:
285
+ # If a previous record in the results already matched all identifiers, then throw an exception.
286
+ if unique_record is not None:
287
+ raise SapioException(f"More than one record of type {wrapper_type.get_wrapper_data_type_name()} "
288
+ f"encountered in system that matches all provided identifiers.")
289
+ unique_record = result
290
+
384
291
  # If a unique record matched the identifiers, return it.
385
292
  if unique_record is not None:
386
293
  return unique_record
@@ -422,29 +329,6 @@ class RecordHandler:
422
329
  return_dict[model] = model.get_parents_of_type(parent_type)
423
330
  return return_dict
424
331
 
425
- @staticmethod
426
- def map_by_parent(models: Iterable[RecordModel], parent_type: type[WrappedType]) \
427
- -> dict[WrappedType, RecordModel]:
428
- """
429
- Take a list of record models and map them by their parent. Essentially an inversion of map_to_parent.
430
- If two records share the same parent, an exception will be thrown. The parents must already be loaded.
431
-
432
- :param models: A list of record models.
433
- :param parent_type: The record model wrapper of the parents.
434
- :return: A dict[ParentType, ModelType]. If an input model doesn't have a parent of the given parent type,
435
- then it will not be in the resulting dictionary.
436
- """
437
- to_parent: dict[RecordModel, WrappedType] = RecordHandler.map_to_parent(models, parent_type)
438
- by_parent: dict[WrappedType, RecordModel] = {}
439
- for record, parent in to_parent.items():
440
- if parent is None:
441
- continue
442
- if parent in by_parent:
443
- raise SapioException(f"Parent {parent.data_type_name} {parent.record_id} encountered more than once "
444
- f"in models list.")
445
- by_parent[parent] = record
446
- return by_parent
447
-
448
332
  @staticmethod
449
333
  def map_by_parents(models: Iterable[RecordModel], parent_type: type[WrappedType]) \
450
334
  -> dict[WrappedType, list[RecordModel]]:
@@ -495,29 +379,6 @@ class RecordHandler:
495
379
  return_dict[model] = model.get_children_of_type(child_type)
496
380
  return return_dict
497
381
 
498
- @staticmethod
499
- def map_by_child(models: Iterable[RecordModel], child_type: type[WrappedType]) \
500
- -> dict[WrappedType, list[RecordModel]]:
501
- """
502
- Take a list of record models and map them by their children. Essentially an inversion of map_to_child.
503
- If two records share the same child, an exception will be thrown. The children must already be loaded.
504
-
505
- :param models: A list of record models.
506
- :param child_type: The record model wrapper of the children.
507
- :return: A dict[ChildType, ModelType]. If an input model doesn't have a child of the given child type,
508
- then it will not be in the resulting dictionary.
509
- """
510
- to_child: dict[RecordModel, WrappedType] = RecordHandler.map_to_child(models, child_type)
511
- by_child: dict[WrappedType, RecordModel] = {}
512
- for record, child in to_child.items():
513
- if child is None:
514
- continue
515
- if child in by_child:
516
- raise SapioException(f"Child {child.data_type_name} {child.record_id} encountered more than once "
517
- f"in models list.")
518
- by_child[child] = record
519
- return by_child
520
-
521
382
  @staticmethod
522
383
  def map_by_children(models: Iterable[RecordModel], child_type: type[WrappedType]) \
523
384
  -> dict[WrappedType, list[RecordModel]]:
@@ -555,8 +416,8 @@ class RecordHandler:
555
416
  return return_dict
556
417
 
557
418
  @staticmethod
558
- def map_by_forward_side_links(models: Iterable[WrappedRecordModel], field_name: str,
559
- side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
419
+ def map_by_forward_side_link(models: Iterable[WrappedRecordModel], field_name: str,
420
+ side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
560
421
  """
561
422
  Take a list of record models and map them by their forward side link. Essentially an inversion of
562
423
  map_to_forward_side_link. Input models that share a forward side link will end up in the same list.
@@ -568,44 +429,18 @@ class RecordHandler:
568
429
  :return: A dict[SideLink, list[ModelType]]. If an input model doesn't have a forward side link of the given type
569
430
  pointing to it, then it will not be in the resulting dictionary.
570
431
  """
571
- to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
432
+ to_side_link: dict[RecordModel, WrappedType] = RecordHandler\
572
433
  .map_to_forward_side_link(models, field_name, side_link_type)
573
- by_side_link: dict[WrappedType, list[WrappedRecordModel]] = {}
434
+ by_side_link: dict[WrappedType, list[RecordModel]] = {}
574
435
  for record, side_link in to_side_link.items():
575
436
  if side_link is None:
576
437
  continue
577
438
  by_side_link.setdefault(side_link, []).append(record)
578
439
  return by_side_link
579
440
 
580
- @staticmethod
581
- def map_by_forward_side_link(models: Iterable[WrappedRecordModel], field_name: str,
582
- side_link_type: type[WrappedType]) -> dict[WrappedType, WrappedRecordModel]:
583
- """
584
- Take a list of record models and map them by their forward side link. Essentially an inversion of
585
- map_to_forward_side_link, but if two records share the same forward link, an exception is thrown.
586
- The forward side link must already be loaded.
587
-
588
- :param models: A list of record models.
589
- :param field_name: The field name on the record models where the side link is located.
590
- :param side_link_type: The record model wrapper of the forward side links.
591
- :return: A dict[SideLink, ModelType]. If an input model doesn't have a forward side link of the given type
592
- pointing to it, then it will not be in the resulting dictionary.
593
- """
594
- to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
595
- .map_to_forward_side_link(models, field_name, side_link_type)
596
- by_side_link: dict[WrappedType, WrappedRecordModel] = {}
597
- for record, side_link in to_side_link.items():
598
- if side_link is None:
599
- continue
600
- if side_link in by_side_link:
601
- raise SapioException(f"Side link {side_link.data_type_name} {side_link.record_id} encountered more "
602
- f"than once in models list.")
603
- by_side_link[side_link] = record
604
- return by_side_link
605
-
606
441
  @staticmethod
607
442
  def map_to_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: str,
608
- side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, list[WrappedType]]:
443
+ side_link_type: type[WrappedType]) -> dict[RecordModel, list[WrappedRecordModel]]:
609
444
  """
610
445
  Map a list of record models to a list reverse side links of a given type. The reverse side links must already
611
446
  be loaded.
@@ -622,29 +457,6 @@ class RecordHandler:
622
457
  return_dict[model] = model.get_reverse_side_link(field_name, side_link_type)
623
458
  return return_dict
624
459
 
625
- @staticmethod
626
- def map_to_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: str,
627
- side_link_type: type[WrappedType]) -> dict[WrappedRecordModel, WrappedType]:
628
- """
629
- Map a list of record models to the reverse side link of a given type. If a given record has more than one
630
- reverse side link of this type, an exception is thrown. The reverse side links must already be loaded.
631
-
632
- :param models: A list of record models.
633
- :param field_name: The field name on the side linked model where the side link to the given record models is
634
- located.
635
- :param side_link_type: The record model wrapper of the reverse side links.
636
- :return: A dict[ModelType, SideLink]. If an input model doesn't have reverse side links of the given type,
637
- then it will map to None.
638
- """
639
- return_dict: dict[WrappedRecordModel, WrappedType] = {}
640
- for model in models:
641
- links: list[WrappedType] = model.get_reverse_side_link(field_name, side_link_type)
642
- if len(links) > 1:
643
- raise SapioException(f"Model {model.data_type_name} {model.record_id} has more than one reverse link "
644
- f"of type {side_link_type.get_wrapper_data_type_name()}.")
645
- return_dict[model] = links[0] if links else None
646
- return return_dict
647
-
648
460
  @staticmethod
649
461
  def map_by_reverse_side_links(models: Iterable[WrappedRecordModel], field_name: str,
650
462
  side_link_type: type[WrappedType]) -> dict[WrappedType, list[WrappedRecordModel]]:
@@ -660,41 +472,14 @@ class RecordHandler:
660
472
  :return: A dict[SideLink, list[ModelType]]. If an input model doesn't have reverse side links of the given type
661
473
  pointing to it, then it will not be in the resulting dictionary.
662
474
  """
663
- to_side_links: dict[WrappedRecordModel, list[WrappedType]] = RecordHandler\
475
+ to_side_links: dict[RecordModel, list[WrappedType]] = RecordHandler\
664
476
  .map_to_reverse_side_links(models, field_name, side_link_type)
665
- by_side_links: dict[WrappedType, list[WrappedRecordModel]] = {}
477
+ by_side_links: dict[WrappedType, list[RecordModel]] = {}
666
478
  for record, side_links in to_side_links.items():
667
479
  for side_link in side_links:
668
480
  by_side_links.setdefault(side_link, []).append(record)
669
481
  return by_side_links
670
482
 
671
- @staticmethod
672
- def map_by_reverse_side_link(models: Iterable[WrappedRecordModel], field_name: str,
673
- side_link_type: type[WrappedType]) -> dict[WrappedType, WrappedRecordModel]:
674
- """
675
- Take a list of record models and map them by their reverse side link. Essentially an inversion of
676
- map_to_reverse_side_link. If two records share the same reverse side link, an exception is thrown.
677
- The reverse side links must already be loaded.
678
-
679
- :param models: A list of record models.
680
- :param field_name: The field name on the side linked model where the side link to the given record models is
681
- located.
682
- :param side_link_type: The record model wrapper of the reverse side links.
683
- :return: A dict[SideLink, ModelType]. If an input model doesn't have a reverse side link of the given type
684
- pointing to it, then it will not be in the resulting dictionary.
685
- """
686
- to_side_link: dict[WrappedRecordModel, WrappedType] = RecordHandler\
687
- .map_to_reverse_side_link(models, field_name, side_link_type)
688
- by_side_link: dict[WrappedType, WrappedRecordModel] = {}
689
- for record, side_link in to_side_link.items():
690
- if side_link is None:
691
- continue
692
- if side_link in by_side_link:
693
- raise SapioException(f"Side link {side_link.data_type_name} {side_link.record_id} encountered more "
694
- f"than once in models list.")
695
- by_side_link[side_link] = record
696
- return by_side_link
697
-
698
483
  @staticmethod
699
484
  def map_by_id(models: Iterable[SapioRecord]) -> dict[int, SapioRecord]:
700
485
  """
@@ -905,7 +690,7 @@ class RecordHandler:
905
690
  relationship path must already be loaded.
906
691
 
907
692
  The path is "flattened" by only following the first record at each step. Useful for traversing 1-to-Many-to-1
908
- relationships (e.g. a sample which is aliquoted to a number of samples, then those aliquots are pooled back
693
+ relationships (e.g. a sample with is aliquoted to a number of samples, then those aliquots are pooled back
909
694
  together into a single sample).
910
695
 
911
696
  Currently, the relationship path may only contain parent/child nodes.
@@ -934,28 +719,10 @@ class RecordHandler:
934
719
  ret_dict.update({model: self.inst_man.wrap(current[0], wrapper_type) if current else None})
935
720
  return ret_dict
936
721
 
937
- def __find_model(self, wrapper_type: type[WrappedType], primary_identifier: str, id_value: Any,
938
- secondary_identifiers: FieldMap | None = None) -> WrappedType | None:
939
- """
940
- Find a record from the system that matches the given field values. The primary identifier and value is used
941
- to query for the record, then the secondary identifiers may be optionally provided to further filter the
942
- returned results. If no record is found with these filters, returns None.
943
- """
944
- # Query for all records that match the primary identifier.
945
- results: list[WrappedType] = self.query_models(wrapper_type, primary_identifier, [id_value])
946
-
947
- # Find the one record, if any, that matches the secondary identifiers.
948
- unique_record: WrappedType | None = None
949
- for result in results:
950
- matches_all: bool = True
951
- for field, value in secondary_identifiers.items():
952
- if result.get_field_value(field) != value:
953
- matches_all = False
954
- break
955
- if matches_all:
956
- # If a previous record in the results already matched all identifiers, then throw an exception.
957
- if unique_record is not None:
958
- raise SapioException(f"More than one record of type {wrapper_type.get_wrapper_data_type_name()} "
959
- f"encountered in system that matches all provided identifiers.")
960
- unique_record = result
961
- return unique_record
722
+ def __exhaust_query_pages(self, data_type_name: str, field: str, value_list: list[Any],
723
+ paging_criteria: DataRecordPojoPageCriteria | None,
724
+ page_limit: int | None) \
725
+ -> tuple[list[DataRecord], DataRecordPojoPageCriteria | None]:
726
+ pager = QueryDataRecordsAutoPager(data_type_name, field, value_list, self.user, paging_criteria)
727
+ pager.max_page = page_limit
728
+ return pager.get_all_at_once(), pager.next_page_criteria
@@ -47,7 +47,6 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
47
47
  # Wrap the execution of each webhook in a try/catch. If an exception occurs, handle any special sapiopycommons
48
48
  # exceptions. Otherwise, return a generic message stating that an error occurred.
49
49
  try:
50
- self.initialize(context)
51
50
  return self.execute(context)
52
51
  except SapioUserErrorException as e:
53
52
  return self.handle_user_error_exception(e)
@@ -58,13 +57,6 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
58
57
  except Exception as e:
59
58
  return self.handle_unexpected_exception(e)
60
59
 
61
- def initialize(self, context: SapioWebhookContext) -> None:
62
- """
63
- A function that can be optionally overridden by your webhooks to initialize additional instance variables,
64
- or set up whatever else you wish to set up before the execute function is ran. Default behavior does nothing.
65
- """
66
- pass
67
-
68
60
  @abstractmethod
69
61
  def execute(self, context: SapioWebhookContext) -> SapioWebhookResult:
70
62
  """
@@ -99,8 +91,7 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
99
91
  if result is not None:
100
92
  return result
101
93
  self.log_error(traceback.format_exc())
102
- if self.can_send_client_callback():
103
- DataMgmtServer.get_client_callback(self.context.user).display_error(e.args[0])
94
+ DataMgmtServer.get_client_callback(self.context.user).display_error(e.args[0])
104
95
  return SapioWebhookResult(False)
105
96
 
106
97
  def handle_unexpected_exception(self, e: Exception) -> SapioWebhookResult:
@@ -246,51 +237,3 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
246
237
  :return: True if this endpoint was invoked as a scheduled action.
247
238
  """
248
239
  return self.context.end_point_type == WebhookEndpointType.SCHEDULEDPLUGIN
249
-
250
- def is_action_button_field(self) -> bool:
251
- """
252
- :return: True if this endpoint was invoked as an action button field.
253
- """
254
- return self.context.end_point_type == WebhookEndpointType.ACTIONDATAFIELD
255
-
256
- def is_action_text_field(self) -> bool:
257
- """
258
- :return: True if this endpoint was invoked as an action text field.
259
- """
260
- return self.context.end_point_type == WebhookEndpointType.ACTION_TEXT_FIELD
261
-
262
- def is_custom(self) -> bool:
263
- """
264
- :return: True if this endpoint was invoked from a custom point, such as a custom queue.
265
- """
266
- return self.context.end_point_type == WebhookEndpointType.CUSTOM
267
-
268
- def is_calendar_event_click_handler(self) -> bool:
269
- """
270
- :return: True if this endpoint was invoked from a calendar event click handler.
271
- """
272
- return self.context.end_point_type == WebhookEndpointType.CALENDAR_EVENT_CLICK_HANDLER
273
-
274
- def is_eln_menu_grabber(self) -> bool:
275
- """
276
- :return: True if this endpoint was invoked as a notebook entry grabber.
277
- """
278
- return self.context.end_point_type == WebhookEndpointType.NOTEBOOKEXPERIMENTGRABBER
279
-
280
- def is_conversation_bot(self) -> bool:
281
- """
282
- :return: True if this endpoint was invoked as from a conversation bot.
283
- """
284
- return self.context.end_point_type == WebhookEndpointType.CONVERSATION_BOT
285
-
286
- def is_multi_data_type_table_toolbar(self) -> bool:
287
- """
288
- :return: True if this endpoint was invoked as a multi data type table toolbar button.
289
- """
290
- return self.context.end_point_type == WebhookEndpointType.REPORTTOOLBAR
291
-
292
- def can_send_client_callback(self) -> bool:
293
- """
294
- :return: Whether client callbacks and directives can be sent from this webhook's endpoint type.
295
- """
296
- return self.context.is_client_callback_available
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2024.8.27a312
3
+ Version: 2024.8.28a313
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>
@@ -17,8 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
18
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Python: >=3.10
20
- Requires-Dist: databind>=4.5
21
- Requires-Dist: sapiopylib>=2024.5.24.210
20
+ Requires-Dist: sapiopylib>=2023.12.13.174
22
21
  Description-Content-Type: text/markdown
23
22
 
24
23
 
@@ -51,7 +50,6 @@ This license does not provide any rights to use any other copyrighted artifacts
51
50
  ## Dependencies
52
51
  The following dependencies are required for this package:
53
52
  - [sapiopylib - The official Sapio Informatics Platform Python API package.](https://pypi.org/project/sapiopylib/)
54
- - [databind - Databind is a library inspired by jackson-databind to de-/serialize Python dataclasses.](https://pypi.org/project/databind/)
55
53
 
56
54
  ## Getting Help
57
55
  If you have a support contract with Sapio Sciences, please use our [technical support channels](https://sapio-sciences.atlassian.net/servicedesk/customer/portals).
@@ -1,43 +1,38 @@
1
1
  sapiopycommons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
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
3
+ sapiopycommons/callbacks/callback_util.py,sha256=D6whWxYWvs5rXOG2Slpi1icC18SLKmG9-MP9f0YDDNE,43256
4
+ sapiopycommons/chem/IndigoMolecules.py,sha256=ukZcX6TMEgkNdD1L1GnH3tp5rGplFNPlGoChAHXbsxw,1945
5
+ sapiopycommons/chem/Molecules.py,sha256=tOkn3fg4QizgqjkRLuvRdVy0JpTD3QEOSvZPxmIyT4c,8607
6
6
  sapiopycommons/chem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  sapiopycommons/datatype/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  sapiopycommons/datatype/attachment_util.py,sha256=YlnMprj5IGBbAZDLG2khS1P7JIYTw_NYfpJAfRZfP3M,3219
9
9
  sapiopycommons/eln/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  sapiopycommons/eln/experiment_handler.py,sha256=v1pG4qtZb8OSNWfKtFo6NjnEkReqnu5R9i_hqWh_xxg,57198
11
- sapiopycommons/eln/experiment_report_util.py,sha256=FTLw-6SLAMeoWTOO-qhGROE9g54pZdyoQJIhiIzlwGw,7848
12
11
  sapiopycommons/eln/plate_designer.py,sha256=FYJfhhNq8hdfuXgDYOYHy6g0m2zNwQXZWF_MTPzElDg,7184
13
12
  sapiopycommons/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
13
  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
14
+ sapiopycommons/files/file_bridge.py,sha256=6yjUi0ejypb1nvcEvn21EuquB-SmEjB-fCZiMaNZg7Q,5757
17
15
  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
16
+ sapiopycommons/files/file_util.py,sha256=92SzwRif4dOcGqZ9ri90QeC20NOCenT8DxQjdSH5Uyc,25556
17
+ sapiopycommons/files/file_validator.py,sha256=5DUI_h0WB1AvfoPgx8En3-sC5xlzm5Z2deoSf9qviKQ,24499
20
18
  sapiopycommons/files/file_writer.py,sha256=5u_iZXTQvuUU7ceHZr8Q001_tvgJhOqBwAnB_pxcAbQ,16027
21
19
  sapiopycommons/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- sapiopycommons/general/accession_service.py,sha256=HYgyOsH_UaoRnoury-c2yTW8SeG4OtjLemdpCzoV4R8,13484
23
20
  sapiopycommons/general/aliases.py,sha256=i6af5o2oVFGNcyk7GkvTWXQs0H9xTbFKc_GIah8NKVU,3594
24
- sapiopycommons/general/custom_report_util.py,sha256=cLgIR5Fn3M9uyAtgfTYRv3JRk2SKNevnsb_R5zidSYs,15557
21
+ sapiopycommons/general/custom_report_util.py,sha256=Yrq-Ize1M1jh9g3BmQT9Egedufi3Nl9xNmgNI_LGiho,4828
25
22
  sapiopycommons/general/exceptions.py,sha256=DOlLKnpCatxQF-lVCToa8ryJgusWLvip6N_1ALN00QE,1679
26
23
  sapiopycommons/general/popup_util.py,sha256=-mN5IgYPrLrOEHJ4CHPi2rec4_WAN6X0yMxHwD5h3Bs,30126
27
24
  sapiopycommons/general/storage_util.py,sha256=ovmK_jN7v09BoX07XxwShpBUC5WYQOM7dbKV_VeLXJU,8892
28
25
  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
26
  sapiopycommons/processtracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
27
  sapiopycommons/processtracking/endpoints.py,sha256=g5h_uCVByqacYm9zWAz8TyAdRsGfaO2o0b5RSJdOaSA,10926
33
28
  sapiopycommons/recordmodel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- sapiopycommons/recordmodel/record_handler.py,sha256=AyK1H3x-g1eu1Mt9XD1h57yRrZp_TJjZlEaQ2kPP4Dc,54432
29
+ sapiopycommons/recordmodel/record_handler.py,sha256=VYUJ0bgZbyc6-XYRKvsxrpWHLdCwxzhv13Ce2tZpAQQ,39348
35
30
  sapiopycommons/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
31
  sapiopycommons/rules/eln_rule_handler.py,sha256=qfkBZtck0KK1i9s9Xe2UZqkzQOgPCzDxRkhxE8Si1xk,10671
37
32
  sapiopycommons/rules/on_save_rule_handler.py,sha256=JY9F30IcHwFVdgPAMQtTYuRastV1jeezhVktyrzNASU,10763
38
33
  sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- sapiopycommons/webhook/webhook_handlers.py,sha256=ibpBY3Sk3Eij919bIdW0awzlogYoQSWYDDOg--NwsQE,13431
40
- sapiopycommons-2024.8.27a312.dist-info/METADATA,sha256=l7W9c2MXyyCiy-ef9TZL0Xdp9-ZcQsLjDPI_nsQNet4,3176
41
- sapiopycommons-2024.8.27a312.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
42
- sapiopycommons-2024.8.27a312.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
43
- sapiopycommons-2024.8.27a312.dist-info/RECORD,,
34
+ sapiopycommons/webhook/webhook_handlers.py,sha256=K_K7CEAMZ-bNb2LCIKdt0CxHsBKkwSBzfnp0JSdGJUM,11102
35
+ sapiopycommons-2024.8.28a313.dist-info/METADATA,sha256=xb9YgxmbGyTKDDaq4vOrxEk5wNz-8KQ_-6WPBgMFexg,3009
36
+ sapiopycommons-2024.8.28a313.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
37
+ sapiopycommons-2024.8.28a313.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
38
+ sapiopycommons-2024.8.28a313.dist-info/RECORD,,