gpp-client 26.9.0.dev2__tar.gz → 26.9.0.dev3__tar.gz

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 (114) hide show
  1. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/PKG-INFO +1 -1
  2. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/scheduler.py +104 -21
  3. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/client.py +10 -3
  4. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_scheduler_programs.py +1 -0
  5. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/.gitignore +0 -0
  6. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/LICENSE +0 -0
  7. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/README.md +0 -0
  8. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/graphql/README.md +0 -0
  9. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/pyproject.toml +0 -0
  10. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/__init__.py +0 -0
  11. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/__init__.py +0 -0
  12. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/cli.py +0 -0
  13. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/__init__.py +0 -0
  14. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/attachment.py +0 -0
  15. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/goats.py +0 -0
  16. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/observation.py +0 -0
  17. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/program.py +0 -0
  18. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/scheduler.py +0 -0
  19. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/site_status.py +0 -0
  20. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/target.py +0 -0
  21. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/commands/workflow_state.py +0 -0
  22. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/console.py +0 -0
  23. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/output.py +0 -0
  24. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/cli/utils.py +0 -0
  25. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/client.py +0 -0
  26. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/constants.py +0 -0
  27. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/__init__.py +0 -0
  28. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/atom.py +0 -0
  29. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/attachment.py +0 -0
  30. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/base.py +0 -0
  31. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/goats.py +0 -0
  32. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/observation.py +0 -0
  33. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/program.py +0 -0
  34. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/site_status.py +0 -0
  35. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/target.py +0 -0
  36. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/domains/workflow_state.py +0 -0
  37. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/environment.py +0 -0
  38. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/exceptions.py +0 -0
  39. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/__init__.py +0 -0
  40. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/async_base_client.py +0 -0
  41. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/base_model.py +0 -0
  42. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/base_operation.py +0 -0
  43. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/clone_observation.py +0 -0
  44. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/clone_target.py +0 -0
  45. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/create_call_for_proposals.py +0 -0
  46. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/create_observation.py +0 -0
  47. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/create_program.py +0 -0
  48. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/create_target_by_program_id.py +0 -0
  49. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/create_target_by_program_reference.py +0 -0
  50. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/create_target_by_proposal_reference.py +0 -0
  51. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/custom_fields.py +0 -0
  52. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/custom_mutations.py +0 -0
  53. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/custom_queries.py +0 -0
  54. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/custom_typing_fields.py +0 -0
  55. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/delete_call_for_proposals_by_id.py +0 -0
  56. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/delete_observation_by_id.py +0 -0
  57. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/delete_observation_by_reference.py +0 -0
  58. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/delete_program_by_id.py +0 -0
  59. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/delete_target_by_id.py +0 -0
  60. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/enums.py +0 -0
  61. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/exceptions.py +0 -0
  62. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/fragments.py +0 -0
  63. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_call_for_proposals.py +0 -0
  64. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_calls_for_proposals.py +0 -0
  65. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_goats_config_options.py +0 -0
  66. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_goats_configuration_requests.py +0 -0
  67. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_goats_observations.py +0 -0
  68. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_goats_programs.py +0 -0
  69. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_observation.py +0 -0
  70. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_observation_attachments_by_id.py +0 -0
  71. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_observation_attachments_by_reference.py +0 -0
  72. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_observation_workflow_state_by_id.py +0 -0
  73. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_observation_workflow_state_by_reference.py +0 -0
  74. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_observations.py +0 -0
  75. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_program_attachments_by_id.py +0 -0
  76. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_program_attachments_by_proposal_reference.py +0 -0
  77. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_program_attachments_by_reference.py +0 -0
  78. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_program_by_id.py +0 -0
  79. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_program_by_proposal_reference.py +0 -0
  80. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_program_by_reference.py +0 -0
  81. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_programs.py +0 -0
  82. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_scheduler_all_programs_id.py +0 -0
  83. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_target_by_id.py +0 -0
  84. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/get_targets.py +0 -0
  85. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/input_types.py +0 -0
  86. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/obs_calculation_update.py +0 -0
  87. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/observation_edit.py +0 -0
  88. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/package_environment.py +0 -0
  89. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/ping.py +0 -0
  90. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/program_edit.py +0 -0
  91. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/restore_call_for_proposals_by_id.py +0 -0
  92. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/restore_observation_by_id.py +0 -0
  93. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/restore_observation_by_reference.py +0 -0
  94. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/restore_program_by_id.py +0 -0
  95. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/restore_target_by_id.py +0 -0
  96. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/scheduler_observations_updates.py +0 -0
  97. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/set_observation_workflow_state.py +0 -0
  98. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/target_edit.py +0 -0
  99. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_call_for_proposals_by_id.py +0 -0
  100. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_calls_for_proposals.py +0 -0
  101. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_observation_by_id.py +0 -0
  102. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_observation_by_reference.py +0 -0
  103. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_observations.py +0 -0
  104. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_program_by_id.py +0 -0
  105. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_programs.py +0 -0
  106. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_target_by_id.py +0 -0
  107. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/generated/update_targets.py +0 -0
  108. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/logging_utils.py +0 -0
  109. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/rest/__init__.py +0 -0
  110. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/rest/client.py +0 -0
  111. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/rest/models.py +0 -0
  112. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/rest/utils.py +0 -0
  113. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/settings.py +0 -0
  114. {gpp_client-26.9.0.dev2 → gpp_client-26.9.0.dev3}/src/gpp_client/urls.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gpp-client
3
- Version: 26.9.0.dev2
3
+ Version: 26.9.0.dev3
4
4
  Summary: Gemini Program Platform client.
5
5
  Project-URL: Homepage, https://github.com/gemini-hlsw/gpp-client
6
6
  Project-URL: Source, https://github.com/gemini-hlsw/gpp-client
@@ -5,7 +5,7 @@ Module for retrieving scheduler information.
5
5
  __all__ = ["SchedulerDomain"]
6
6
 
7
7
  from datetime import datetime
8
- from typing import Any, AsyncIterator
8
+ from typing import Any, AsyncIterator, Awaitable, Callable
9
9
 
10
10
  from gpp_client.domains.base import BaseDomain
11
11
  from gpp_client.rest.models import VisibilityChanges, parse_visibility_changes
@@ -16,40 +16,112 @@ from gpp_client.generated import (
16
16
  from gpp_client.generated.get_scheduler_all_programs_id import (
17
17
  GetSchedulerAllProgramsId,
18
18
  )
19
- from gpp_client.generated.get_scheduler_programs import GetSchedulerPrograms
19
+ from gpp_client.generated.get_scheduler_programs import (
20
+ GetSchedulerPrograms,
21
+ GetSchedulerProgramsPrograms,
22
+ )
20
23
  from gpp_client.generated.input_types import (
21
24
  ObservationWorkflowState,
22
25
  WhereCalculatedObservationWorkflow,
23
26
  WhereObservation,
24
- WhereOrderObservationId,
25
27
  WhereOrderObservationWorkflowState,
28
+ WhereOrderProgramId,
29
+ WhereProgram,
26
30
  )
27
31
 
32
+ # Each program carries its full group tree, so large pages are heavy for the ODB.
33
+ PROGRAMS_PAGE_SIZE = 1000
34
+ OBSERVATIONS_PAGE_SIZE = 1000
35
+
28
36
 
29
37
  class SchedulerDomain(BaseDomain):
30
38
  """
31
39
  Domain for retrieving scheduler information.
32
40
  """
33
41
 
42
+ @staticmethod
43
+ async def _fetch_all_pages(
44
+ fetch_page: Callable[[str | None, int], Awaitable[Any]],
45
+ page_size: int,
46
+ ) -> list[Any]:
47
+ """
48
+ Walk an ``OFFSET``/``LIMIT`` selection one page at a time and return
49
+ every match.
50
+
51
+ Parameters
52
+ ----------
53
+ fetch_page : Callable[[str | None, int], Awaitable[Any]]
54
+ Coroutine taking ``offset`` and ``limit`` and returning a select
55
+ result with ``matches`` and ``has_more``.
56
+ page_size : int
57
+ Number of matches requested per page. Must be at least 2.
58
+
59
+ Returns
60
+ -------
61
+ list[Any]
62
+ Every match across all pages, without repeats.
63
+
64
+ Raises
65
+ ------
66
+ ValueError
67
+ If ``page_size`` is lower than 2.
68
+ """
69
+ # OFFSET is inclusive, so every page after the first repeats the previous
70
+ # page's last match. A page of 1 would never advance.
71
+ if page_size < 2:
72
+ raise ValueError(f"page_size must be at least 2, got {page_size}.")
73
+
74
+ matches = []
75
+ offset = None
76
+ while True:
77
+ result = await fetch_page(offset, page_size)
78
+ page_matches = result.matches
79
+ if offset is not None and page_matches and page_matches[0].id == offset:
80
+ page_matches = page_matches[1:]
81
+ matches.extend(page_matches)
82
+
83
+ if not result.has_more or not page_matches:
84
+ return matches
85
+ offset = page_matches[-1].id
86
+
34
87
  async def get_programs(
35
88
  self,
36
89
  *,
37
90
  programs_list: list[str] | None = None,
91
+ page_size: int = PROGRAMS_PAGE_SIZE,
38
92
  ) -> GetSchedulerPrograms:
39
93
  """
40
- Get scheduler programs.
94
+ Get scheduler programs, fetching every page so the result reads as a
95
+ single query.
41
96
 
42
97
  Parameters
43
98
  ----------
44
99
  programs_list : list[str] | None, optional
45
100
  Optional list of program IDs to restrict the result set.
101
+ page_size : int, optional
102
+ Number of programs requested per page. Must be at least 2.
46
103
 
47
104
  Returns
48
105
  -------
49
106
  GetSchedulerPrograms
50
- The generated GraphQL response model.
107
+ The generated GraphQL response model holding every matching program.
108
+
109
+ Raises
110
+ ------
111
+ ValueError
112
+ If ``page_size`` is lower than 2.
51
113
  """
52
- return await self._graphql.get_scheduler_programs(programs_list=programs_list)
114
+
115
+ async def fetch_page(offset: str | None, limit: int) -> Any:
116
+ page = await self._graphql.get_scheduler_programs(
117
+ programs_list=programs_list, offset=offset, limit=limit
118
+ )
119
+ return page.programs
120
+
121
+ matches = await self._fetch_all_pages(fetch_page, page_size)
122
+ return GetSchedulerPrograms(
123
+ programs=GetSchedulerProgramsPrograms(matches=matches, has_more=False)
124
+ )
53
125
 
54
126
  async def get_program_ids(
55
127
  self,
@@ -178,6 +250,7 @@ class SchedulerDomain(BaseDomain):
178
250
  async def get_all(
179
251
  self,
180
252
  programs_list: list | None = None,
253
+ exclude_programs: list[str] | None = None,
181
254
  ) -> list[dict[str, Any]]:
182
255
  """
183
256
  Fetch all programs with a complete group tree and observations.
@@ -186,6 +259,9 @@ class SchedulerDomain(BaseDomain):
186
259
  ----------
187
260
  programs_list : list, optional
188
261
  Optional filtering clause.
262
+ exclude_programs : list[str], optional
263
+ Program IDs to skip, e.g. one whose observations the ODB cannot
264
+ generate a sequence for, which otherwise fails the whole query.
189
265
 
190
266
  Returns
191
267
  -------
@@ -197,11 +273,13 @@ class SchedulerDomain(BaseDomain):
197
273
  programs_list = [
198
274
  p.id for p in (await self.get_program_ids()).programs.matches
199
275
  ]
276
+ if exclude_programs:
277
+ excluded = set(exclude_programs)
278
+ programs_list = [p for p in programs_list if p not in excluded]
200
279
 
201
280
  response = await self.get_programs(programs_list=programs_list)
202
281
  response = response.model_dump()
203
282
  programs = response["programs"].get("matches", [])
204
- observations = []
205
283
  for program in programs:
206
284
  # Create root group.
207
285
  root = {"name": "root", "elements": []}
@@ -220,16 +298,12 @@ class SchedulerDomain(BaseDomain):
220
298
  elem = obs or g.get("group")
221
299
 
222
300
  groups_elements_mapping[elem["id"]] = g
223
- if elem == obs:
224
- observations.append(elem["id"])
225
301
  else:
226
302
  children_map.setdefault(parent_id, []).append(g)
227
303
  group = g.get("group")
228
304
  if group:
229
305
  # Subgroup that can contain children of their own.
230
306
  groups_elements_mapping[group["id"]] = g
231
- else:
232
- observations.append(g["observation"]["id"])
233
307
 
234
308
  for parent_id, children in children_map.items():
235
309
  if parent_id in groups_elements_mapping:
@@ -244,9 +318,10 @@ class SchedulerDomain(BaseDomain):
244
318
  pass
245
319
  program["root"] = root
246
320
 
247
- # If is in the list and status is Ready or OnGoing.
248
321
  where_observation = WhereObservation(
249
- id=WhereOrderObservationId(in_=observations),
322
+ program=WhereProgram(
323
+ id=WhereOrderProgramId(in_=[p["id"] for p in programs])
324
+ ),
250
325
  workflow=WhereCalculatedObservationWorkflow(
251
326
  workflow_state=WhereOrderObservationWorkflowState(
252
327
  in_=[
@@ -260,17 +335,25 @@ class SchedulerDomain(BaseDomain):
260
335
  ),
261
336
  )
262
337
 
263
- # Get observation data
264
- obs_response = await self._graphql.get_observations(
265
- where=where_observation, include_deleted=False
338
+ # Get observation data, only once every program page is in.
339
+ async def fetch_observations_page(offset: str | None, limit: int) -> Any:
340
+ page = await self._graphql.get_observations(
341
+ where=where_observation,
342
+ offset=offset,
343
+ limit=limit,
344
+ include_deleted=False,
345
+ )
346
+ return page.observations
347
+
348
+ obs_matches = await self._fetch_all_pages(
349
+ fetch_observations_page, OBSERVATIONS_PAGE_SIZE
266
350
  )
267
- obs_payload = obs_response.observations.model_dump()
268
- obs_mapping = {o["id"]: o for o in obs_payload["matches"]}
351
+ obs_mapping = {o.id: o.model_dump() for o in obs_matches}
269
352
 
270
- # Get sequence
271
- if observations:
353
+ # Get sequence for filtered observations (READY/ONGOING)
354
+ if obs_mapping:
272
355
  atom_digest_response = (
273
- await self._rest.get_atom_digests(observations)
356
+ await self._rest.get_atom_digests(list(obs_mapping))
274
357
  ).split("\n")
275
358
  obs_atoms_mapping = self._parse_atom_digest(atom_digest_response)
276
359
  else:
@@ -14254,11 +14254,14 @@ class GraphQLClient(AsyncBaseClient):
14254
14254
  async def get_scheduler_programs(
14255
14255
  self,
14256
14256
  programs_list: Union[Optional[list[Any]], UnsetType] = UNSET,
14257
+ offset: Union[Optional[Any], UnsetType] = UNSET,
14258
+ limit: Union[Optional[Any], UnsetType] = UNSET,
14257
14259
  **kwargs: Any,
14258
14260
  ) -> GetSchedulerPrograms:
14259
14261
  query = gql("""
14260
- query GetSchedulerPrograms($programsList: [ProgramId!]) {
14261
- programs(WHERE: {id: {IN: $programsList}}) {
14262
+ query GetSchedulerPrograms($programsList: [ProgramId!], $offset: ProgramId, $limit: NonNegInt) {
14263
+ programs(WHERE: {id: {IN: $programsList}}, OFFSET: $offset, LIMIT: $limit) {
14264
+ hasMore
14262
14265
  matches {
14263
14266
  id
14264
14267
  name
@@ -14351,7 +14354,11 @@ class GraphQLClient(AsyncBaseClient):
14351
14354
  }
14352
14355
  }
14353
14356
  """)
14354
- variables: dict[str, object] = {"programsList": programs_list}
14357
+ variables: dict[str, object] = {
14358
+ "programsList": programs_list,
14359
+ "offset": offset,
14360
+ "limit": limit,
14361
+ }
14355
14362
  response = await self.execute(
14356
14363
  query=query,
14357
14364
  operation_name="GetSchedulerPrograms",
@@ -18,6 +18,7 @@ class GetSchedulerPrograms(BaseModel):
18
18
 
19
19
 
20
20
  class GetSchedulerProgramsPrograms(BaseModel):
21
+ has_more: bool = Field(alias="hasMore")
21
22
  matches: list["GetSchedulerProgramsProgramsMatches"]
22
23
 
23
24