sapiopycommons 2024.8.19a305__py3-none-any.whl → 2024.8.26a307__py3-none-any.whl

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

Potentially problematic release.


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

@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import io
4
- from typing import Any
5
4
  from weakref import WeakValueDictionary
6
5
 
7
6
  from sapiopylib.rest.ClientCallbackService import ClientCallback
@@ -18,7 +17,6 @@ from sapiopylib.rest.pojo.webhook.ClientCallbackRequest import OptionDialogReque
18
17
  DataRecordDialogRequest, InputSelectionRequest, FilePromptRequest, MultiFilePromptRequest, \
19
18
  TempTableSelectionRequest, DisplayPopupRequest, PopupType
20
19
  from sapiopylib.rest.pojo.webhook.ClientCallbackResult import ESigningResponsePojo
21
- from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
22
20
  from sapiopylib.rest.pojo.webhook.WebhookEnums import FormAccessLevel, ScanToSelectCriteria, SearchType
23
21
  from sapiopylib.rest.utils.DataTypeCacheManager import DataTypeCacheManager
24
22
  from sapiopylib.rest.utils.FormBuilder import FormBuilder
@@ -26,7 +24,8 @@ from sapiopylib.rest.utils.recorddatasinks import InMemoryRecordDataSink
26
24
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
27
25
 
28
26
  from sapiopycommons.files.file_util import FileUtil
29
- from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier
27
+ from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier, FieldValue, \
28
+ UserIdentifier
30
29
  from sapiopycommons.general.custom_report_util import CustomReportUtil
31
30
  from sapiopycommons.general.exceptions import SapioUserCancelledException, SapioException, SapioUserErrorException
32
31
  from sapiopycommons.recordmodel.record_handler import RecordHandler
@@ -42,11 +41,11 @@ class CallbackUtil:
42
41
  __instances: WeakValueDictionary[SapioUser, CallbackUtil] = WeakValueDictionary()
43
42
  __initialized: bool
44
43
 
45
- def __new__(cls, context: SapioWebhookContext | SapioUser):
44
+ def __new__(cls, context: UserIdentifier):
46
45
  """
47
46
  :param context: The current webhook context or a user object to send requests from.
48
47
  """
49
- user = context if isinstance(context, SapioUser) else context.user
48
+ user = AliasUtil.to_sapio_user(context)
50
49
  obj = cls.__instances.get(user)
51
50
  if not obj:
52
51
  obj = object.__new__(cls)
@@ -54,7 +53,7 @@ class CallbackUtil:
54
53
  cls.__instances[user] = obj
55
54
  return obj
56
55
 
57
- def __init__(self, context: SapioWebhookContext | SapioUser):
56
+ def __init__(self, context: UserIdentifier):
58
57
  """
59
58
  :param context: The current webhook context or a user object to send requests from.
60
59
  """
@@ -62,7 +61,7 @@ class CallbackUtil:
62
61
  return
63
62
  self.__initialized = True
64
63
 
65
- self.user = context if isinstance(context, SapioUser) else context.user
64
+ self.user = AliasUtil.to_sapio_user(context)
66
65
  self.callback = DataMgmtServer.get_client_callback(self.user)
67
66
  self.dt_cache = DataTypeCacheManager(self.user)
68
67
  self.width_pixels = None
@@ -281,7 +280,7 @@ class CallbackUtil:
281
280
  modifier = FieldModifier(visible=True, editable=editable)
282
281
 
283
282
  # Build the form using only those fields that are desired.
284
- values: dict[str, Any] = {}
283
+ values: dict[str, FieldValue] = {}
285
284
  builder = FormBuilder(data_type, type_def.display_name, type_def.plural_display_name)
286
285
  for field_name in fields:
287
286
  field_def = field_defs.get(field_name)
@@ -303,7 +302,7 @@ class CallbackUtil:
303
302
  raise SapioUserCancelledException()
304
303
  return response
305
304
 
306
- def input_dialog(self, title: str, msg: str, field: AbstractVeloxFieldDefinition) -> Any:
305
+ def input_dialog(self, title: str, msg: str, field: AbstractVeloxFieldDefinition) -> FieldValue:
307
306
  """
308
307
  Create an input dialog where the user must input data for a singular field.
309
308
 
@@ -314,7 +313,7 @@ class CallbackUtil:
314
313
  """
315
314
  request = InputDialogCriteria(title, msg, field,
316
315
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
317
- response: Any | None = self.callback.show_input_dialog(request)
316
+ response: FieldValue | None = self.callback.show_input_dialog(request)
318
317
  if response is None:
319
318
  raise SapioUserCancelledException()
320
319
  return response
@@ -606,10 +605,10 @@ class CallbackUtil:
606
605
  field_names.append(name)
607
606
 
608
607
  # Get the values for each row.
609
- values: list[dict[str, Any]] = []
608
+ values: list[dict[str, FieldValue]] = []
610
609
  for row in row_contents:
611
610
  # The final values for this row:
612
- row_values: dict[str, Any] = {}
611
+ row_values: dict[str, FieldValue] = {}
613
612
 
614
613
  # Map the records for this row by their data type. If a field map is provided, its data type is Default.
615
614
  row_records: dict[str, SapioRecord | FieldMap] = {}
@@ -901,7 +900,7 @@ class CallbackUtil:
901
900
  return response
902
901
 
903
902
  def request_file(self, title: str, exts: list[str] | None = None,
904
- show_image_editor: bool = False, show_camera_button: bool = False) -> (str, bytes):
903
+ show_image_editor: bool = False, show_camera_button: bool = False) -> tuple[str, bytes]:
905
904
  """
906
905
  Request a single file from the user.
907
906
 
@@ -934,7 +933,7 @@ class CallbackUtil:
934
933
  return file_path, sink.data
935
934
 
936
935
  def request_files(self, title: str, exts: list[str] | None = None,
937
- show_image_editor: bool = False, show_camera_button: bool = False):
936
+ show_image_editor: bool = False, show_camera_button: bool = False) -> dict[str, bytes]:
938
937
  """
939
938
  Request multiple files from the user.
940
939
 
@@ -965,7 +964,7 @@ class CallbackUtil:
965
964
  return ret_dict
966
965
 
967
966
  @staticmethod
968
- def __verify_file(file_path: str, file_bytes: bytes, allowed_extensions: list[str]):
967
+ def __verify_file(file_path: str, file_bytes: bytes, allowed_extensions: list[str]) -> None:
969
968
  """
970
969
  Verify that the provided file was read (i.e. the file path and file bytes aren't None or empty) and that it
971
970
  has the correct file extension. Raises a user error exception if something about the file is incorrect.
@@ -18,7 +18,7 @@ AND = CompositeTermOperation.AND_OPERATOR
18
18
  OR = CompositeTermOperation.OR_OPERATOR
19
19
 
20
20
  # Forms that field term values can take.
21
- TermValue = str | int | float | bool | Iterable
21
+ TermValue = str | int | float | bool | Iterable | None
22
22
 
23
23
 
24
24
  class TermBuilder:
@@ -281,9 +281,12 @@ class TermBuilder:
281
281
  """
282
282
  # If the given value is already a string, then nothing needs to be done with it.
283
283
  if not isinstance(value, str):
284
+ # If the given value is None, then use an empty string for the search instead.
285
+ if value is None:
286
+ value = ""
284
287
  # If the given value is an iterable object, then the return value is the contents of that iterable
285
288
  # in a comma separated list surrounded by curly braces.
286
- if isinstance(value, Iterable):
289
+ elif isinstance(value, Iterable):
287
290
  # When converting a list of values to a string, values in the list which are already strings should be
288
291
  # put in quotation marks so that strings that contain commas do not get split up. All other value
289
292
  # types can be simply converted to a string, though.
@@ -1,11 +1,9 @@
1
1
  import io
2
2
 
3
3
  from sapiopylib.rest.DataMgmtService import DataMgmtServer
4
- from sapiopylib.rest.User import SapioUser
5
- from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
6
4
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
7
5
 
8
- from sapiopycommons.general.aliases import AliasUtil, SapioRecord
6
+ from sapiopycommons.general.aliases import AliasUtil, SapioRecord, UserIdentifier
9
7
  from sapiopycommons.general.exceptions import SapioException
10
8
  from sapiopycommons.recordmodel.record_handler import RecordHandler
11
9
 
@@ -13,7 +11,7 @@ from sapiopycommons.recordmodel.record_handler import RecordHandler
13
11
  # FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
14
12
  class AttachmentUtil:
15
13
  @staticmethod
16
- def get_attachment_bytes(context: SapioWebhookContext | SapioUser, attachment: SapioRecord) -> bytes:
14
+ def get_attachment_bytes(context: UserIdentifier, attachment: SapioRecord) -> bytes:
17
15
  """
18
16
  Get the data bytes for the given attachment record. Makes a webservice call to retrieve the data.
19
17
 
@@ -22,10 +20,7 @@ class AttachmentUtil:
22
20
  :return: The bytes for the attachment's file data.
23
21
  """
24
22
  attachment = AliasUtil.to_data_record(attachment)
25
- if isinstance(context, SapioWebhookContext):
26
- dr_man = context.data_record_manager
27
- else:
28
- dr_man = DataMgmtServer.get_data_record_manager(context)
23
+ dr_man = DataMgmtServer.get_data_record_manager(AliasUtil.to_sapio_user(context))
29
24
  with io.BytesIO() as data_sink:
30
25
  def consume_data(chunk: bytes):
31
26
  data_sink.write(chunk)
@@ -36,7 +31,7 @@ class AttachmentUtil:
36
31
  return file_bytes
37
32
 
38
33
  @staticmethod
39
- def set_attachment_bytes(context: SapioWebhookContext | SapioUser, attachment: SapioRecord,
34
+ def set_attachment_bytes(context: UserIdentifier, attachment: SapioRecord,
40
35
  file_name: str, file_bytes: bytes) -> None:
41
36
  """
42
37
  Set the attachment data for a given attachment record. Makes a webservice call to set the data.
@@ -50,15 +45,12 @@ class AttachmentUtil:
50
45
  raise SapioException("Provided record cannot have its attachment data set, as it does not exist in the "
51
46
  "system yet.")
52
47
  attachment = AliasUtil.to_data_record(attachment)
53
- if isinstance(context, SapioWebhookContext):
54
- dr_man = context.data_record_manager
55
- else:
56
- dr_man = DataMgmtServer.get_data_record_manager(context)
48
+ dr_man = DataMgmtServer.get_data_record_manager(AliasUtil.to_sapio_user(context))
57
49
  with io.BytesIO(file_bytes) as stream:
58
50
  dr_man.set_attachment_data(attachment, file_name, stream)
59
51
 
60
52
  @staticmethod
61
- def create_attachment(context: SapioWebhookContext | SapioUser, file_name: str, file_bytes: bytes,
53
+ def create_attachment(context: UserIdentifier, file_name: str, file_bytes: bytes,
62
54
  wrapper_type: type[WrappedType]) -> WrappedType:
63
55
  """
64
56
  Create an attachment data type and initialize its attachment bytes at the same time.
@@ -23,7 +23,8 @@ from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelInst
23
23
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
24
24
  from sapiopylib.rest.utils.recordmodel.properties import Child
25
25
 
26
- from sapiopycommons.general.aliases import AliasUtil, SapioRecord, ExperimentIdentifier, RecordModel
26
+ from sapiopycommons.general.aliases import AliasUtil, SapioRecord, ExperimentIdentifier, UserIdentifier, \
27
+ DataTypeIdentifier, RecordModel
27
28
  from sapiopycommons.general.exceptions import SapioException
28
29
 
29
30
  Step = str | ElnEntryStep
@@ -87,8 +88,7 @@ class ExperimentHandler:
87
88
  __instances: WeakValueDictionary[str, ExperimentHandler] = WeakValueDictionary()
88
89
  __initialized: bool
89
90
 
90
- def __new__(cls, context: SapioWebhookContext | SapioUser,
91
- experiment: ExperimentIdentifier | SapioRecord | None = None):
91
+ def __new__(cls, context: UserIdentifier, experiment: ExperimentIdentifier | SapioRecord | None = None):
92
92
  """
93
93
  :param context: The current webhook context or a user object to send requests from.
94
94
  :param experiment: If an experiment is provided that is separate from the experiment that is in the context,
@@ -106,8 +106,7 @@ class ExperimentHandler:
106
106
  cls.__instances[key] = obj
107
107
  return obj
108
108
 
109
- def __init__(self, context: SapioWebhookContext | SapioUser,
110
- experiment: ExperimentIdentifier | SapioRecord | None = None):
109
+ def __init__(self, context: UserIdentifier, experiment: ExperimentIdentifier | SapioRecord | None = None):
111
110
  """
112
111
  Initialization will throw an exception if there is no ELN Experiment in the provided context and no experiment
113
112
  is provided.
@@ -146,8 +145,7 @@ class ExperimentHandler:
146
145
  self.__steps.update({entry.entry_name: ElnEntryStep(self.__protocol, entry)})
147
146
 
148
147
  @staticmethod
149
- def __parse_params(context: SapioWebhookContext | SapioUser,
150
- experiment: ExperimentIdentifier | SapioRecord | None = None) \
148
+ def __parse_params(context: UserIdentifier, experiment: ExperimentIdentifier | SapioRecord | None = None) \
151
149
  -> tuple[SapioUser, SapioWebhookContext | None, ElnExperiment]:
152
150
  if isinstance(context, SapioWebhookContext):
153
151
  user = context.user
@@ -332,11 +330,7 @@ class ExperimentHandler:
332
330
  :return: The data record for this experiment. None if it has no record.
333
331
  """
334
332
  if not hasattr(self, "_ExperimentHandler__exp_record"):
335
- drm = DataMgmtServer.get_data_record_manager(self.user)
336
- dt = self.__eln_exp.experiment_data_type_name
337
- results = drm.query_data_records_by_id(dt, [self.__eln_exp.experiment_record_id]).result_list
338
- # PR-46504: Set the exp_record to None if there are no results.
339
- self.__exp_record = results[0] if results else None
333
+ self.__exp_record = self.__protocol.get_record()
340
334
  if self.__exp_record is None and exception_on_none:
341
335
  raise SapioException(f"Experiment record not found for experiment with ID {self.__exp_id}.")
342
336
  return self.__exp_record
@@ -536,7 +530,7 @@ class ExperimentHandler:
536
530
  ret_list.append(step)
537
531
  return ret_list
538
532
 
539
- def get_all_steps(self, data_type: str | type[WrappedType] | None = None) -> list[ElnEntryStep]:
533
+ def get_all_steps(self, data_type: DataTypeIdentifier | None = None) -> list[ElnEntryStep]:
540
534
  """
541
535
  Get a list of every entry in the experiment. Optionally filter the returned entries by a data type.
542
536
 
@@ -552,8 +546,7 @@ class ExperimentHandler:
552
546
  all_steps: list[ElnEntryStep] = self.__protocol.get_sorted_step_list()
553
547
  if data_type is None:
554
548
  return all_steps
555
- if not isinstance(data_type, str):
556
- data_type: str = data_type.get_wrapper_data_type_name()
549
+ data_type: str = AliasUtil.to_data_type_name(data_type)
557
550
  return [x for x in all_steps if data_type in x.get_data_type_names()]
558
551
 
559
552
  def get_step_records(self, step: Step) -> list[DataRecord]:
@@ -605,6 +598,10 @@ class ExperimentHandler:
605
598
  The records may be provided as either DataRecords, PyRecordModels, or WrappedRecordModels.
606
599
  """
607
600
  step = self.__to_eln_step(step)
601
+ dt: str = AliasUtil.to_singular_data_type_name(records)
602
+ if dt != step.get_data_type_names()[0]:
603
+ raise SapioException(f"Cannot add {dt} records to entry {step.get_name()} of type "
604
+ f"{step.get_data_type_names()[0]}.")
608
605
  step.add_records(AliasUtil.to_data_records(records))
609
606
 
610
607
  def remove_step_records(self, step: Step, records: Iterable[SapioRecord]) -> None:
@@ -623,6 +620,10 @@ class ExperimentHandler:
623
620
  The records may be provided as either DataRecords, PyRecordModels, or WrappedRecordModels.
624
621
  """
625
622
  step = self.__to_eln_step(step)
623
+ dt: str = AliasUtil.to_singular_data_type_name(records)
624
+ if dt != step.get_data_type_names()[0]:
625
+ raise SapioException(f"Cannot remove {dt} records from entry {step.get_name()} of type "
626
+ f"{step.get_data_type_names()[0]}.")
626
627
  step.remove_records(AliasUtil.to_data_records(records))
627
628
 
628
629
  def set_step_records(self, step: Step, records: Iterable[SapioRecord]) -> None:
@@ -646,6 +647,10 @@ class ExperimentHandler:
646
647
  The records may be provided as either DataRecords, PyRecordModels, or WrappedRecordModels.
647
648
  """
648
649
  step = self.__to_eln_step(step)
650
+ dt: str = AliasUtil.to_singular_data_type_name(records)
651
+ if dt != step.get_data_type_names()[0]:
652
+ raise SapioException(f"Cannot set {dt} records for entry {step.get_name()} of type "
653
+ f"{step.get_data_type_names()[0]}.")
649
654
  step.set_records(AliasUtil.to_data_records(records))
650
655
 
651
656
  # FR-46496 - Provide alias of set_step_records for use with form entries.
@@ -733,8 +738,10 @@ class ExperimentHandler:
733
738
  dt: str = step.get_data_type_names()[0]
734
739
  if not ElnBaseDataType.is_eln_type(dt):
735
740
  raise SapioException("The provided step is not an ELN data type entry.")
736
- if any([x.data_type_name != dt for x in records]):
737
- raise SapioException("Not all of the provided records match the data type of the step.")
741
+ record_dt: str = AliasUtil.to_singular_data_type_name(records)
742
+ if record_dt != dt:
743
+ raise SapioException(f"Cannot remove {dt} records from entry {step.get_name()} of type "
744
+ f"{step.get_data_type_names()[0]}.")
738
745
  # If any rows were provided as data records, turn them into record models before deleting them, as otherwise
739
746
  # this function would need to make a webservice call to do the deletion.
740
747
  data_records: list[DataRecord] = []
@@ -1,19 +1,10 @@
1
1
  from sapiopylib.rest.User import SapioUser
2
- from sapiopylib.rest.pojo.CustomReport import (
3
- CompositeReportTerm,
4
- CompositeTermOperation,
5
- CustomReportCriteria,
6
- ExplicitJoinDefinition,
7
- FieldCompareReportTerm,
8
- RawReportTerm,
9
- RawTermOperation,
10
- ReportColumn,
11
- )
12
2
  from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
13
- from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
14
3
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
15
4
 
16
- from sapiopycommons.general.aliases import SapioRecord
5
+ from sapiopycommons.customreport.custom_report_builder import CustomReportBuilder
6
+ from sapiopycommons.customreport.term_builder import TermBuilder
7
+ from sapiopycommons.general.aliases import SapioRecord, UserIdentifier, AliasUtil
17
8
  from sapiopycommons.general.custom_report_util import CustomReportUtil
18
9
  from sapiopycommons.recordmodel.record_handler import RecordHandler
19
10
 
@@ -25,10 +16,8 @@ _RECORD_ID = "RECORDID"
25
16
  # that given records were used in or getting all records of a datatype used in given experiments.
26
17
  class ExperimentReportUtil:
27
18
  @staticmethod
28
- def map_records_to_experiment_ids(
29
- context: SapioWebhookContext | SapioUser,
30
- records: list[SapioRecord],
31
- ) -> dict[SapioRecord, list[int]]:
19
+ def map_records_to_experiment_ids(context: UserIdentifier, records: list[SapioRecord]) \
20
+ -> dict[SapioRecord, list[int]]:
32
21
  """
33
22
  Return a dictionary mapping each record to a list of ids of experiments that they were used in.
34
23
  If a record wasn't used in any experiments then it will be mapped to an empty list.
@@ -40,38 +29,25 @@ class ExperimentReportUtil:
40
29
  if not records:
41
30
  return {}
42
31
 
43
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
44
-
45
- data_type_name = records[0].data_type_name
32
+ user: SapioUser = AliasUtil.to_sapio_user(context)
33
+ data_type_name: str = AliasUtil.to_singular_data_type_name(records)
46
34
 
47
35
  record_ids = [record.record_id for record in records]
48
-
49
- rows = ExperimentReportUtil.__get_record_experiment_relation_rows(
50
- user, data_type_name, record_ids=record_ids
51
- )
36
+ rows = ExperimentReportUtil.__get_record_experiment_relation_rows(user, data_type_name, record_ids=record_ids)
52
37
 
53
38
  id_to_record: dict[int, SapioRecord] = RecordHandler.map_by_id(records)
54
-
55
- record_to_exps: dict[SapioRecord, set[int]] = {
56
- record: set() for record in records
57
- }
58
-
39
+ record_to_exps: dict[SapioRecord, set[int]] = {record: set() for record in records}
59
40
  for row in rows:
60
41
  record_id: int = row[_RECORD_ID]
61
42
  exp_id: int = row[_NOTEBOOK_ID]
62
-
63
43
  record = id_to_record[record_id]
64
-
65
44
  record_to_exps[record].add(exp_id)
66
45
 
67
46
  return {record: list(exps) for record, exps in record_to_exps.items()}
68
47
 
69
48
  @staticmethod
70
- def map_experiments_to_records_of_type(
71
- context: SapioWebhookContext | SapioUser,
72
- exp_ids: list[int],
73
- wrapper_type: type[WrappedType],
74
- ) -> dict[int, list[WrappedType]]:
49
+ def map_experiments_to_records_of_type(context: UserIdentifier, exp_ids: list[int],
50
+ wrapper_type: type[WrappedType]) -> dict[int, list[WrappedType]]:
75
51
  """
76
52
  Return a dictionary mapping each experiment id to a list of records of the given type that were used in each experiment.
77
53
  If an experiment didn't use any records of the given type then it will be mapped to an empty list.
@@ -84,41 +60,27 @@ class ExperimentReportUtil:
84
60
  if not exp_ids:
85
61
  return {}
86
62
 
87
- user = context if isinstance(context, SapioUser) else context.user
88
-
63
+ user = AliasUtil.to_sapio_user(context)
89
64
  record_handler = RecordHandler(user)
90
-
91
65
  data_type_name: str = wrapper_type.get_wrapper_data_type_name()
92
66
 
93
- rows = ExperimentReportUtil.__get_record_experiment_relation_rows(
94
- user, data_type_name, exp_ids=exp_ids
95
- )
96
-
67
+ rows = ExperimentReportUtil.__get_record_experiment_relation_rows(user, data_type_name, exp_ids=exp_ids)
97
68
  record_ids: set[int] = {row[_RECORD_ID] for row in rows}
98
-
99
69
  records = record_handler.query_models_by_id(wrapper_type, record_ids)
100
70
 
101
71
  id_to_record: dict[int, WrappedType] = RecordHandler.map_by_id(records)
102
-
103
72
  exp_to_records: dict[int, set[SapioRecord]] = {exp: set() for exp in exp_ids}
104
-
105
73
  for row in rows:
106
74
  record_id: int = row[_RECORD_ID]
107
75
  exp_id: int = row[_NOTEBOOK_ID]
108
-
109
76
  record = id_to_record[record_id]
110
-
111
77
  exp_to_records[exp_id].add(record)
112
78
 
113
79
  return {exp: list(records) for exp, records in exp_to_records.items()}
114
80
 
115
81
  @staticmethod
116
- def __get_record_experiment_relation_rows(
117
- user: SapioUser,
118
- data_type_name: str,
119
- record_ids: list[int] | None = None,
120
- exp_ids: list[int] | None = None,
121
- ) -> list[dict[str, int]]:
82
+ def __get_record_experiment_relation_rows(user: SapioUser, data_type_name: str, record_ids: list[int] | None = None,
83
+ exp_ids: list[int] | None = None) -> list[dict[str, int]]:
122
84
  """
123
85
  Return a list of dicts mapping \"RECORDID\" to the record id and \"EXPERIMENTID\" to the experiment id.
124
86
  At least one of record_ids and exp_ids should be provided.
@@ -126,89 +88,31 @@ class ExperimentReportUtil:
126
88
  assert (record_ids or exp_ids)
127
89
 
128
90
  if record_ids:
129
- rec_ids = [str(record_id) for record_id in record_ids]
130
-
131
- ids_str = "{" + ", ".join(rec_ids) + "}"
132
-
133
- records_term = RawReportTerm(
134
- data_type_name, "RECORDID", RawTermOperation.EQUAL_TO_OPERATOR, ids_str
135
- )
136
-
91
+ records_term = TermBuilder.is_term(data_type_name, "RECORDID", record_ids)
137
92
  else:
138
93
  # Get all records of the given type
139
- records_term = RawReportTerm(
140
- data_type_name,
141
- "RECORDID",
142
- RawTermOperation.GREATER_THAN_OR_EQUAL_OPERATOR,
143
- "0",
144
- )
94
+ records_term = TermBuilder.all_records_term(data_type_name)
145
95
 
146
96
  if exp_ids:
147
- exp_ids = [str(exp_id) for exp_id in exp_ids]
148
-
149
- ids_str = "{" + ", ".join(exp_ids) + "}"
150
-
151
- exp_term = RawReportTerm(
152
- "NOTEBOOKEXPERIMENT",
153
- "EXPERIMENTID",
154
- RawTermOperation.EQUAL_TO_OPERATOR,
155
- ids_str,
156
- )
157
-
97
+ exp_term = TermBuilder.is_term("NOTEBOOKEXPERIMENT", "EXPERIMENTID", exp_ids)
158
98
  else:
159
99
  # Get all experiments
160
- exp_term = RawReportTerm(
161
- "NOTEBOOKEXPERIMENT",
162
- "EXPERIMENTID",
163
- RawTermOperation.GREATER_THAN_OR_EQUAL_OPERATOR,
164
- "0",
165
- )
166
-
167
- root_term = CompositeReportTerm(
168
- records_term, CompositeTermOperation.AND_OPERATOR, exp_term
169
- )
170
-
171
- # The columns the resulting dataframe will have
172
- column_list = [
173
- ReportColumn(data_type_name, "RECORDID", FieldType.LONG),
174
- ReportColumn("NOTEBOOKEXPERIMENT", "EXPERIMENTID", FieldType.LONG),
175
- ]
100
+ exp_term = TermBuilder.gte_term("NOTEBOOKEXPERIMENT", "EXPERIMENTID", "0")
176
101
 
177
- # Join records on the experiment entry records that correspond to them.
178
- records_entry_join = FieldCompareReportTerm(
179
- data_type_name,
180
- "RECORDID",
181
- RawTermOperation.EQUAL_TO_OPERATOR,
182
- "EXPERIMENTENTRYRECORD",
183
- "RECORDID",
184
- )
102
+ root_term = TermBuilder.and_terms(records_term, exp_term)
185
103
 
104
+ # Join records on the experiment entry records that correspond to them.
105
+ records_entry_join = TermBuilder.compare_is_term("EXPERIMENTENTRYRECORD", "RECORDID", data_type_name, "RECORDID")
186
106
  # Join entry records on the experiment entries they are in.
187
- experiment_entry_enb_entry_join = FieldCompareReportTerm(
188
- "EXPERIMENTENTRYRECORD",
189
- "ENTRYID",
190
- RawTermOperation.EQUAL_TO_OPERATOR,
191
- "ENBENTRY",
192
- "ENTRYID",
193
- )
194
-
107
+ experiment_entry_enb_entry_join = TermBuilder.compare_is_term("ENBENTRY", "ENTRYID", "EXPERIMENTENTRYRECORD", "ENTRYID")
195
108
  # Join entries on the experiments they are in.
196
- enb_entry_experiment_join = FieldCompareReportTerm(
197
- "ENBENTRY",
198
- "EXPERIMENTID",
199
- RawTermOperation.EQUAL_TO_OPERATOR,
200
- "NOTEBOOKEXPERIMENT",
201
- "EXPERIMENTID",
202
- )
203
-
204
- report_criteria = CustomReportCriteria(
205
- column_list,
206
- root_term,
207
- join_list=[
208
- ExplicitJoinDefinition("EXPERIMENTENTRYRECORD", records_entry_join),
209
- ExplicitJoinDefinition("ENBENTRY", experiment_entry_enb_entry_join),
210
- ExplicitJoinDefinition("NOTEBOOKEXPERIMENT", enb_entry_experiment_join),
211
- ],
212
- )
213
-
214
- return CustomReportUtil.run_custom_report(user, report_criteria)
109
+ enb_entry_experiment_join = TermBuilder.compare_is_term("NOTEBOOKEXPERIMENT", "EXPERIMENTID", "ENBENTRY", "EXPERIMENTID")
110
+
111
+ report_builder = CustomReportBuilder(data_type_name)
112
+ report_builder.set_root_term(root_term)
113
+ report_builder.add_column("RECORDID", FieldType.LONG, data_type=data_type_name)
114
+ report_builder.add_column("EXPERIMENTID", FieldType.LONG, data_type="NOTEBOOKEXPERIMENT")
115
+ report_builder.add_join(records_entry_join)
116
+ report_builder.add_join(experiment_entry_enb_entry_join)
117
+ report_builder.add_join(enb_entry_experiment_join)
118
+ return CustomReportUtil.run_custom_report(user, report_builder.build_report_criteria())
@@ -1,12 +1,13 @@
1
1
  import io
2
2
 
3
3
  from sapiopylib.rest.User import SapioUser
4
- from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
4
+
5
+ from sapiopycommons.general.aliases import UserIdentifier, AliasUtil
5
6
 
6
7
 
7
8
  class CDL:
8
9
  @staticmethod
9
- def load_cdl(context: SapioWebhookContext | SapioUser, config_name: str, file_name: str, file_data: bytes | str) \
10
+ def load_cdl(context: UserIdentifier, config_name: str, file_name: str, file_data: bytes | str) \
10
11
  -> list[int]:
11
12
  """
12
13
  Create data records from a file using one of the complex data loader (CDL) configurations in the system.
@@ -22,7 +23,7 @@ class CDL:
22
23
  "configName": config_name,
23
24
  "fileName": file_name
24
25
  }
25
- user: SapioUser = context if isinstance(context, SapioUser) else context.user
26
+ user: SapioUser = AliasUtil.to_sapio_user(context)
26
27
  with io.BytesIO(file_data.encode() if isinstance(file_data, str) else file_data) as data_stream:
27
28
  response = user.post_data_stream(sub_path, params=params, data_stream=data_stream)
28
29
  user.raise_for_status(response)