UncountablePythonSDK 0.0.133__py3-none-any.whl → 0.0.136.dev0__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 (31) hide show
  1. examples/integration-server/jobs/materials_auto/example_cron.py +1 -1
  2. examples/integration-server/jobs/materials_auto/example_runsheet_wh.py +51 -14
  3. pkgs/type_spec/builder.py +15 -1
  4. uncountable/integration/cli.py +60 -1
  5. uncountable/integration/queue_runner/command_server/command_client.py +24 -0
  6. uncountable/integration/queue_runner/command_server/command_server.py +34 -0
  7. uncountable/integration/queue_runner/command_server/protocol/command_server.proto +15 -0
  8. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +9 -3
  9. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +25 -0
  10. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +45 -0
  11. uncountable/integration/queue_runner/command_server/types.py +21 -1
  12. uncountable/integration/queue_runner/datastore/datastore_sqlite.py +25 -0
  13. uncountable/integration/queue_runner/datastore/interface.py +3 -0
  14. uncountable/integration/queue_runner/job_scheduler.py +36 -1
  15. uncountable/integration/queue_runner/types.py +2 -0
  16. uncountable/integration/queue_runner/worker.py +28 -26
  17. uncountable/integration/scheduler.py +64 -13
  18. uncountable/integration/telemetry.py +79 -0
  19. uncountable/types/__init__.py +2 -2
  20. uncountable/types/api/files/download_file.py +15 -1
  21. uncountable/types/api/listing/fetch_listing.py +1 -2
  22. uncountable/types/api/runsheet/export_default_runsheet.py +44 -0
  23. uncountable/types/client_base.py +56 -0
  24. uncountable/types/listing.py +37 -0
  25. uncountable/types/listing_t.py +483 -1
  26. {uncountablepythonsdk-0.0.133.dist-info → uncountablepythonsdk-0.0.136.dev0.dist-info}/METADATA +2 -2
  27. {uncountablepythonsdk-0.0.133.dist-info → uncountablepythonsdk-0.0.136.dev0.dist-info}/RECORD +29 -30
  28. uncountable/types/structured_filters.py +0 -25
  29. uncountable/types/structured_filters_t.py +0 -248
  30. {uncountablepythonsdk-0.0.133.dist-info → uncountablepythonsdk-0.0.136.dev0.dist-info}/WHEEL +0 -0
  31. {uncountablepythonsdk-0.0.133.dist-info → uncountablepythonsdk-0.0.136.dev0.dist-info}/top_level.txt +0 -0
@@ -87,30 +87,32 @@ def run_queued_job(
87
87
  base_span=span,
88
88
  profile_metadata=job_details.profile_metadata,
89
89
  job_definition=job_details.job_definition,
90
+ queued_job_uuid=queued_job.queued_job_uuid,
90
91
  )
91
- try:
92
- client = construct_uncountable_client(
93
- profile_meta=job_details.profile_metadata, logger=job_logger
94
- )
95
- batch_processor = AsyncBatchProcessor(client=client)
96
-
97
- payload = _resolve_queued_job_payload(queued_job)
98
-
99
- args = JobArguments(
100
- job_definition=job_details.job_definition,
101
- client=client,
102
- batch_processor=batch_processor,
103
- profile_metadata=job_details.profile_metadata,
104
- logger=job_logger,
105
- payload=payload,
106
- job_uuid=queued_job.queued_job_uuid,
107
- )
108
-
109
- return execute_job(
110
- args=args,
111
- profile_metadata=job_details.profile_metadata,
112
- job_definition=job_details.job_definition,
113
- )
114
- except BaseException as e:
115
- job_logger.log_exception(e)
116
- return job_definition_t.JobResult(success=False)
92
+ with job_logger.resource_tracking():
93
+ try:
94
+ client = construct_uncountable_client(
95
+ profile_meta=job_details.profile_metadata, logger=job_logger
96
+ )
97
+ batch_processor = AsyncBatchProcessor(client=client)
98
+
99
+ payload = _resolve_queued_job_payload(queued_job)
100
+
101
+ args = JobArguments(
102
+ job_definition=job_details.job_definition,
103
+ client=client,
104
+ batch_processor=batch_processor,
105
+ profile_metadata=job_details.profile_metadata,
106
+ logger=job_logger,
107
+ payload=payload,
108
+ job_uuid=queued_job.queued_job_uuid,
109
+ )
110
+
111
+ return execute_job(
112
+ args=args,
113
+ profile_metadata=job_details.profile_metadata,
114
+ job_definition=job_details.job_definition,
115
+ )
116
+ except BaseException as e:
117
+ job_logger.log_exception(e)
118
+ return job_definition_t.JobResult(success=False)
@@ -6,6 +6,7 @@ import time
6
6
  from dataclasses import dataclass
7
7
  from datetime import UTC
8
8
  from enum import StrEnum
9
+ from typing import assert_never
9
10
 
10
11
  from opentelemetry.trace import get_current_span
11
12
 
@@ -16,6 +17,7 @@ from uncountable.integration.queue_runner.command_server import (
16
17
  check_health,
17
18
  )
18
19
  from uncountable.integration.queue_runner.queue_runner import start_queue_runner
20
+ from uncountable.integration.queue_runner.types import RESTART_EXIT_CODE
19
21
  from uncountable.integration.telemetry import Logger
20
22
 
21
23
  SHUTDOWN_TIMEOUT_SECS = 30
@@ -55,6 +57,19 @@ class ProcessInfo:
55
57
  return self.process.poll()
56
58
 
57
59
 
60
+ @dataclass(kw_only=True)
61
+ class ProcessAlarmRestart:
62
+ process: ProcessInfo
63
+
64
+
65
+ @dataclass(kw_only=True)
66
+ class ProcessAlarmShutdownAll:
67
+ pass
68
+
69
+
70
+ ProcessAlarm = ProcessAlarmRestart | ProcessAlarmShutdownAll
71
+
72
+
58
73
  def handle_shutdown(logger: Logger, processes: dict[ProcessName, ProcessInfo]) -> None:
59
74
  logger.log_info("received shutdown command, shutting down sub-processes")
60
75
  for proc_info in processes.values():
@@ -129,12 +144,21 @@ def restart_process(
129
144
  logger.log_info("uwsgi restarted successfully")
130
145
 
131
146
 
132
- def check_process_alive(
147
+ def check_process_alarms(
133
148
  logger: Logger, processes: dict[ProcessName, ProcessInfo]
134
- ) -> None:
149
+ ) -> ProcessAlarm | None:
135
150
  for proc_info in processes.values():
136
151
  if not proc_info.is_alive:
137
- restart_process(logger, proc_info, processes)
152
+ if proc_info.exitcode == RESTART_EXIT_CODE:
153
+ logger.log_warning(
154
+ f"process {proc_info.name} requested restart! restarting"
155
+ )
156
+ return ProcessAlarmRestart(process=proc_info)
157
+ logger.log_error(
158
+ f"process {proc_info.name} shut down unexpectedly! shutting down scheduler; exit code is {proc_info.exitcode}"
159
+ )
160
+ return ProcessAlarmShutdownAll()
161
+ return None
138
162
 
139
163
 
140
164
  def _wait_queue_runner_online() -> None:
@@ -166,16 +190,24 @@ def main() -> None:
166
190
  processes[process.name] = process
167
191
  logger.log_info(f"started process {process.name}")
168
192
 
169
- runner_process = multiprocessing.Process(target=start_queue_runner)
170
- runner_process.start()
171
- add_process(ProcessInfo(name=ProcessName.QUEUE_RUNNER, process=runner_process))
193
+ def _start_queue_runner() -> None:
194
+ runner_process = multiprocessing.Process(target=start_queue_runner)
195
+ runner_process.start()
196
+ add_process(
197
+ ProcessInfo(
198
+ name=ProcessName.QUEUE_RUNNER,
199
+ process=runner_process,
200
+ )
201
+ )
172
202
 
173
- try:
174
- _wait_queue_runner_online()
175
- except Exception as e:
176
- logger.log_exception(e)
177
- handle_shutdown(logger, processes=processes)
178
- return
203
+ try:
204
+ _wait_queue_runner_online()
205
+ except Exception as e:
206
+ logger.log_exception(e)
207
+ handle_shutdown(logger, processes=processes)
208
+ return
209
+
210
+ _start_queue_runner()
179
211
 
180
212
  cron_process = multiprocessing.Process(target=cron_target)
181
213
  cron_process.start()
@@ -189,7 +221,26 @@ def main() -> None:
189
221
 
190
222
  try:
191
223
  while True:
192
- check_process_alive(logger, processes=processes)
224
+ process_alarm = check_process_alarms(logger, processes=processes)
225
+ match process_alarm:
226
+ case ProcessAlarmRestart():
227
+ match process_alarm.process.name:
228
+ case ProcessName.QUEUE_RUNNER:
229
+ del processes[ProcessName.QUEUE_RUNNER]
230
+ _start_queue_runner()
231
+ case ProcessName.CRON_SERVER | ProcessName.UWSGI:
232
+ raise NotImplementedError(
233
+ f"restarting {process_alarm.process.name} not yet implemented"
234
+ )
235
+ case _:
236
+ assert_never(process_alarm.process.name)
237
+ case ProcessAlarmShutdownAll():
238
+ handle_shutdown(logger, processes)
239
+ sys.exit(1)
240
+ case None:
241
+ pass
242
+ case _:
243
+ assert_never(process_alarm)
193
244
  time.sleep(1)
194
245
  except KeyboardInterrupt:
195
246
  handle_shutdown(logger, processes=processes)
@@ -1,13 +1,16 @@
1
1
  import functools
2
2
  import json
3
3
  import os
4
+ import threading
4
5
  import time
5
6
  import traceback
7
+ import types
6
8
  import typing
7
9
  from contextlib import contextmanager
8
10
  from enum import StrEnum
9
11
  from typing import Generator, assert_never, cast
10
12
 
13
+ import psutil
11
14
  from opentelemetry import _logs, trace
12
15
  from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
13
16
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
@@ -185,6 +188,76 @@ class Logger:
185
188
  yield self
186
189
 
187
190
 
191
+ class PerJobResourceTracker:
192
+ def __init__(self, logger: "JobLogger", sample_interval: float = 0.5) -> None:
193
+ self.logger = logger
194
+ self.sample_interval = sample_interval
195
+ self._process = psutil.Process(os.getpid())
196
+ self._stop_event = threading.Event()
197
+ self._thread: threading.Thread | None = None
198
+
199
+ self.max_rss: int = 0
200
+ self.start_cpu_times: psutil._common.pcputimes | None = None
201
+ self.end_cpu_times: psutil._common.pcputimes | None = None
202
+ self.start_wall_time: float | None = None
203
+ self.end_wall_time: float | None = None
204
+
205
+ def start(self) -> None:
206
+ self.start_cpu_times = self._process.cpu_times()
207
+ self.start_wall_time = time.monotonic()
208
+
209
+ def _monitor() -> None:
210
+ try:
211
+ while not self._stop_event.is_set():
212
+ rss = self._process.memory_info().rss
213
+ self.max_rss = max(self.max_rss, rss)
214
+ time.sleep(self.sample_interval)
215
+ except Exception:
216
+ self._stop_event.set()
217
+
218
+ self._thread = threading.Thread(target=_monitor, daemon=True)
219
+ self._thread.start()
220
+
221
+ def stop(self) -> None:
222
+ self._stop_event.set()
223
+ if self._thread is not None:
224
+ self._thread.join()
225
+ self.end_cpu_times = self._process.cpu_times()
226
+ self.end_wall_time = time.monotonic()
227
+
228
+ def __enter__(self) -> typing.Self:
229
+ self.start()
230
+ return self
231
+
232
+ def __exit__(
233
+ self,
234
+ exc_type: type[BaseException] | None,
235
+ exc_value: BaseException | None,
236
+ traceback: types.TracebackType | None,
237
+ ) -> None:
238
+ self.stop()
239
+ stats = dict(self.summary())
240
+ self.logger.log_info("Job resource usage summary", attributes=stats)
241
+
242
+ def summary(self) -> Attributes:
243
+ assert self.start_cpu_times is not None
244
+ assert self.end_cpu_times is not None
245
+ assert self.start_wall_time is not None
246
+ assert self.end_wall_time is not None
247
+
248
+ cpu_user = self.end_cpu_times.user - self.start_cpu_times.user
249
+ cpu_sys = self.end_cpu_times.system - self.start_cpu_times.system
250
+ cpu_total = cpu_user + cpu_sys
251
+ elapsed = self.end_wall_time - self.start_wall_time
252
+ return {
253
+ "cpu_user_s": round(cpu_user, 3),
254
+ "cpu_system_s": round(cpu_sys, 3),
255
+ "cpu_total_s": round(cpu_total, 3),
256
+ "wall_time_s": round(elapsed, 3),
257
+ "peak_rss_mb": round(self.max_rss / (1024 * 1024), 2),
258
+ }
259
+
260
+
188
261
  class JobLogger(Logger):
189
262
  def __init__(
190
263
  self,
@@ -192,9 +265,11 @@ class JobLogger(Logger):
192
265
  base_span: Span,
193
266
  profile_metadata: job_definition_t.ProfileMetadata,
194
267
  job_definition: job_definition_t.JobDefinition,
268
+ queued_job_uuid: str,
195
269
  ) -> None:
196
270
  self.profile_metadata = profile_metadata
197
271
  self.job_definition = job_definition
272
+ self.queued_job_uuid = queued_job_uuid
198
273
  super().__init__(base_span)
199
274
 
200
275
  def _patch_attributes(
@@ -214,6 +289,7 @@ class JobLogger(Logger):
214
289
  patched_attributes["job.name"] = self.job_definition.name
215
290
  patched_attributes["job.id"] = self.job_definition.id
216
291
  patched_attributes["job.definition_type"] = self.job_definition.type
292
+ patched_attributes["job.queued_job_uuid"] = self.queued_job_uuid
217
293
  match self.job_definition:
218
294
  case job_definition_t.CronJobDefinition():
219
295
  patched_attributes["job.definition.cron_spec"] = (
@@ -239,6 +315,9 @@ class JobLogger(Logger):
239
315
  assert_never(self.job_definition.executor)
240
316
  return _cast_attributes(patched_attributes)
241
317
 
318
+ def resource_tracking(self) -> PerJobResourceTracker:
319
+ return PerJobResourceTracker(self)
320
+
242
321
 
243
322
  @contextmanager
244
323
  def push_scope_optional(
@@ -37,6 +37,7 @@ from . import entity_t as entity_t
37
37
  from .api.batch import execute_batch as execute_batch_t
38
38
  from .api.batch import execute_batch_load_async as execute_batch_load_async_t
39
39
  from . import experiment_groups_t as experiment_groups_t
40
+ from .api.runsheet import export_default_runsheet as export_default_runsheet_t
40
41
  from .api.entity import export_entities as export_entities_t
41
42
  from . import exports_t as exports_t
42
43
  from .api.listing import fetch_listing as fetch_listing_t
@@ -121,7 +122,6 @@ from .api.recipes import set_recipe_tags as set_recipe_tags_t
121
122
  from .api.recipes import set_recipe_total as set_recipe_total_t
122
123
  from .api.entity import set_values as set_values_t
123
124
  from . import sockets_t as sockets_t
124
- from . import structured_filters_t as structured_filters_t
125
125
  from .api.entity import transition_entity_phase as transition_entity_phase_t
126
126
  from .api.recipes import unarchive_recipes as unarchive_recipes_t
127
127
  from . import units_t as units_t
@@ -172,6 +172,7 @@ __all__: list[str] = [
172
172
  "execute_batch_t",
173
173
  "execute_batch_load_async_t",
174
174
  "experiment_groups_t",
175
+ "export_default_runsheet_t",
175
176
  "export_entities_t",
176
177
  "exports_t",
177
178
  "fetch_listing_t",
@@ -256,7 +257,6 @@ __all__: list[str] = [
256
257
  "set_recipe_total_t",
257
258
  "set_values_t",
258
259
  "sockets_t",
259
- "structured_filters_t",
260
260
  "transition_entity_phase_t",
261
261
  "unarchive_recipes_t",
262
262
  "units_t",
@@ -21,6 +21,7 @@ __all__: list[str] = [
21
21
  "FileDownloadQuery",
22
22
  "FileDownloadQueryBase",
23
23
  "FileDownloadQueryEntityField",
24
+ "FileDownloadQueryTextDocumentId",
24
25
  "FileDownloadQueryType",
25
26
  ]
26
27
 
@@ -31,6 +32,7 @@ ENDPOINT_PATH = "api/external/files/download_file"
31
32
  # DO NOT MODIFY -- This file is generated by type_spec
32
33
  class FileDownloadQueryType(StrEnum):
33
34
  ENTITY_FIELD = "entity_field"
35
+ TEXT_DOCUMENT_ID = "text_document_id"
34
36
 
35
37
 
36
38
  # DO NOT MODIFY -- This file is generated by type_spec
@@ -54,14 +56,26 @@ class FileDownloadQueryEntityField(FileDownloadQueryBase):
54
56
  field_key: identifier_t.IdentifierKey
55
57
 
56
58
 
59
+ # DO NOT MODIFY -- This file is generated by type_spec
60
+ @serial_class(
61
+ named_type_path="sdk.api.files.download_file.FileDownloadQueryTextDocumentId",
62
+ parse_require={"type"},
63
+ )
64
+ @dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
65
+ class FileDownloadQueryTextDocumentId(FileDownloadQueryBase):
66
+ type: typing.Literal[FileDownloadQueryType.TEXT_DOCUMENT_ID] = FileDownloadQueryType.TEXT_DOCUMENT_ID
67
+ text_document_id: base_t.ObjectId
68
+
69
+
57
70
  # DO NOT MODIFY -- This file is generated by type_spec
58
71
  FileDownloadQuery = typing.Annotated[
59
- typing.Union[FileDownloadQueryEntityField],
72
+ FileDownloadQueryEntityField | FileDownloadQueryTextDocumentId,
60
73
  serial_union_annotation(
61
74
  named_type_path="sdk.api.files.download_file.FileDownloadQuery",
62
75
  discriminator="type",
63
76
  discriminator_map={
64
77
  "entity_field": FileDownloadQueryEntityField,
78
+ "text_document_id": FileDownloadQueryTextDocumentId,
65
79
  },
66
80
  ),
67
81
  ]
@@ -11,7 +11,6 @@ from pkgs.serialization import serial_class
11
11
  from ... import base_t
12
12
  from ... import entity_t
13
13
  from ... import listing_t
14
- from ... import structured_filters_t
15
14
 
16
15
  __all__: list[str] = [
17
16
  "Arguments",
@@ -33,7 +32,7 @@ ENDPOINT_PATH = "api/external/listing/fetch_listing"
33
32
  class Arguments:
34
33
  entity_type: entity_t.EntityType
35
34
  columns: list[listing_t.ColumnIdentifier]
36
- filters: structured_filters_t.FilterNode | None = None
35
+ filters: listing_t.FilterNode | None = None
37
36
  limit: int | None = None
38
37
  offset: int | None = None
39
38
 
@@ -0,0 +1,44 @@
1
+ # DO NOT MODIFY -- This file is generated by type_spec
2
+ # ruff: noqa: E402 Q003
3
+ # fmt: off
4
+ # isort: skip_file
5
+ from __future__ import annotations
6
+ import typing # noqa: F401
7
+ import datetime # noqa: F401
8
+ from decimal import Decimal # noqa: F401
9
+ import dataclasses
10
+ from pkgs.serialization import serial_class
11
+ from ... import base_t
12
+ from ... import entity_t
13
+ from ... import identifier_t
14
+
15
+ __all__: list[str] = [
16
+ "Arguments",
17
+ "Data",
18
+ "ENDPOINT_METHOD",
19
+ "ENDPOINT_PATH",
20
+ ]
21
+
22
+ ENDPOINT_METHOD = "POST"
23
+ ENDPOINT_PATH = "api/external/runsheet/export_default_runsheet"
24
+
25
+
26
+ # DO NOT MODIFY -- This file is generated by type_spec
27
+ @serial_class(
28
+ named_type_path="sdk.api.runsheet.export_default_runsheet.Arguments",
29
+ )
30
+ @dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
31
+ class Arguments:
32
+ entities: list[identifier_t.IdentifierKey]
33
+ entity_type: entity_t.EntityType = entity_t.EntityType.RECIPE
34
+ runsheet_key: identifier_t.IdentifierKey
35
+
36
+
37
+ # DO NOT MODIFY -- This file is generated by type_spec
38
+ @serial_class(
39
+ named_type_path="sdk.api.runsheet.export_default_runsheet.Data",
40
+ )
41
+ @dataclasses.dataclass(slots=base_t.ENABLE_SLOTS, kw_only=True) # type: ignore[literal-required]
42
+ class Data:
43
+ text_document_id: base_t.ObjectId
44
+ # DO NOT MODIFY -- This file is generated by type_spec
@@ -37,6 +37,7 @@ import uncountable.types.api.batch.execute_batch as execute_batch_t
37
37
  import uncountable.types.api.batch.execute_batch_load_async as execute_batch_load_async_t
38
38
  import uncountable.types.api.entity.export_entities as export_entities_t
39
39
  from uncountable.types import exports_t
40
+ import uncountable.types.api.listing.fetch_listing as fetch_listing_t
40
41
  from uncountable.types import field_values_t
41
42
  from uncountable.types import generic_upload_t
42
43
  import uncountable.types.api.recipes.get_column_calculation_values as get_column_calculation_values_t
@@ -49,6 +50,7 @@ import uncountable.types.api.inputs.get_input_names as get_input_names_t
49
50
  import uncountable.types.api.inputs.get_inputs_data as get_inputs_data_t
50
51
  import uncountable.types.api.outputs.get_output_data as get_output_data_t
51
52
  import uncountable.types.api.outputs.get_output_names as get_output_names_t
53
+ import uncountable.types.api.outputs.get_output_organization as get_output_organization_t
52
54
  import uncountable.types.api.project.get_projects as get_projects_t
53
55
  import uncountable.types.api.project.get_projects_data as get_projects_data_t
54
56
  import uncountable.types.api.recipes.get_recipe_calculations as get_recipe_calculations_t
@@ -64,6 +66,7 @@ import uncountable.types.api.uploader.invoke_uploader as invoke_uploader_t
64
66
  import uncountable.types.api.entity.list_aggregate as list_aggregate_t
65
67
  import uncountable.types.api.entity.list_entities as list_entities_t
66
68
  import uncountable.types.api.id_source.list_id_source as list_id_source_t
69
+ from uncountable.types import listing_t
67
70
  import uncountable.types.api.entity.lock_entity as lock_entity_t
68
71
  import uncountable.types.api.recipes.lock_recipes as lock_recipes_t
69
72
  import uncountable.types.api.entity.lookup_entity as lookup_entity_t
@@ -738,6 +741,39 @@ class ClientMethods(ABC):
738
741
  )
739
742
  return self.do_request(api_request=api_request, return_type=export_entities_t.Data)
740
743
 
744
+ def fetch_listing(
745
+ self,
746
+ *,
747
+ entity_type: entity_t.EntityType,
748
+ columns: list[listing_t.ColumnIdentifier],
749
+ filters: listing_t.FilterNode | None = None,
750
+ limit: int | None = None,
751
+ offset: int | None = None,
752
+ _request_options: client_config_t.RequestOptions | None = None,
753
+ ) -> fetch_listing_t.Data:
754
+ """External API for fetching a listing based on some structured columns and filters
755
+
756
+ :param entity_type: The entity type to fetch listing entries for
757
+ :param columns: The columns to include in the results
758
+ :param filters: Structured filters to apply to the listing, represented by a FilterNode object
759
+ :param limit: The number of entries to return. If not provided, defaults to 100. Note a maximum of 100 entries can be returned.
760
+ :param offset: The number of entries to skip for pagination
761
+ """
762
+ args = fetch_listing_t.Arguments(
763
+ entity_type=entity_type,
764
+ columns=columns,
765
+ filters=filters,
766
+ limit=limit,
767
+ offset=offset,
768
+ )
769
+ api_request = APIRequest(
770
+ method=fetch_listing_t.ENDPOINT_METHOD,
771
+ endpoint=fetch_listing_t.ENDPOINT_PATH,
772
+ args=args,
773
+ request_options=_request_options,
774
+ )
775
+ return self.do_request(api_request=api_request, return_type=fetch_listing_t.Data)
776
+
741
777
  def get_column_calculation_values(
742
778
  self,
743
779
  *,
@@ -989,6 +1025,26 @@ class ClientMethods(ABC):
989
1025
  )
990
1026
  return self.do_request(api_request=api_request, return_type=get_output_names_t.Data)
991
1027
 
1028
+ def get_output_organization(
1029
+ self,
1030
+ *,
1031
+ request: get_output_organization_t.OutputOrganizationRequest,
1032
+ _request_options: client_config_t.RequestOptions | None = None,
1033
+ ) -> get_output_organization_t.Data:
1034
+ """Returns the output organization for the specified material family, project, or user
1035
+
1036
+ """
1037
+ args = get_output_organization_t.Arguments(
1038
+ request=request,
1039
+ )
1040
+ api_request = APIRequest(
1041
+ method=get_output_organization_t.ENDPOINT_METHOD,
1042
+ endpoint=get_output_organization_t.ENDPOINT_PATH,
1043
+ args=args,
1044
+ request_options=_request_options,
1045
+ )
1046
+ return self.do_request(api_request=api_request, return_type=get_output_organization_t.Data)
1047
+
992
1048
  def get_projects(
993
1049
  self,
994
1050
  *,
@@ -5,5 +5,42 @@
5
5
  # Kept only for SDK backwards compatibility
6
6
  from .listing_t import ColumnType as ColumnType
7
7
  from .listing_t import ColumnIdentifierEntityRefName as ColumnIdentifierEntityRefName
8
+ from .listing_t import ColumnIdentifierTransitive as ColumnIdentifierTransitive
9
+ from .listing_t import LoadAggregateType as LoadAggregateType
10
+ from .listing_t import LoadAggregateGroupBy as LoadAggregateGroupBy
11
+ from .listing_t import LoadAggregateCollect as LoadAggregateCollect
12
+ from .listing_t import LoadAggregateCollectDistinct as LoadAggregateCollectDistinct
13
+ from .listing_t import LoadAggregateCount as LoadAggregateCount
14
+ from .listing_t import LoadAggregateCountDistinct as LoadAggregateCountDistinct
15
+ from .listing_t import LoadAggregateFirst as LoadAggregateFirst
16
+ from .listing_t import LoadAggregateUniqueValue as LoadAggregateUniqueValue
17
+ from .listing_t import LoadAggregateMin as LoadAggregateMin
18
+ from .listing_t import LoadAggregateMax as LoadAggregateMax
19
+ from .listing_t import LoadAggregateMean as LoadAggregateMean
20
+ from .listing_t import LoadAggregateStddev as LoadAggregateStddev
21
+ from .listing_t import LoadAggregateSum as LoadAggregateSum
22
+ from .listing_t import DateGroupType as DateGroupType
23
+ from .listing_t import LoadAggregateGroupByDate as LoadAggregateGroupByDate
24
+ from .listing_t import AggregateLoadTypes as AggregateLoadTypes
25
+ from .listing_t import ColumnIdentifierTransitiveAggregate as ColumnIdentifierTransitiveAggregate
8
26
  from .listing_t import ColumnIdentifier as ColumnIdentifier
27
+ from .listing_t import FilterRelation as FilterRelation
28
+ from .listing_t import FilterSpecBase as FilterSpecBase
29
+ from .listing_t import FilterScalarType as FilterScalarType
30
+ from .listing_t import FilterIdType as FilterIdType
31
+ from .listing_t import StringFilterValue as StringFilterValue
32
+ from .listing_t import FilterSpecEquals as FilterSpecEquals
33
+ from .listing_t import FilterSpecInclude as FilterSpecInclude
34
+ from .listing_t import FilterSpecIStrContains as FilterSpecIStrContains
35
+ from .listing_t import FilterSpecIStrStartsWith as FilterSpecIStrStartsWith
36
+ from .listing_t import FilterSpecExists as FilterSpecExists
37
+ from .listing_t import FilterSpecGreater as FilterSpecGreater
38
+ from .listing_t import FilterSpecLess as FilterSpecLess
39
+ from .listing_t import FilterSpecGeq as FilterSpecGeq
40
+ from .listing_t import FilterSpecLeq as FilterSpecLeq
41
+ from .listing_t import FilterSpec as FilterSpec
42
+ from .listing_t import FilterNodeType as FilterNodeType
43
+ from .listing_t import FilterNodeBase as FilterNodeBase
44
+ from .listing_t import FilterNodeColumnAnd as FilterNodeColumnAnd
45
+ from .listing_t import FilterNode as FilterNode
9
46
  # DO NOT MODIFY -- This file is generated by type_spec