Skip to content

Commit 066b8a2

Browse files
🎨 introduce include_children query parameter for activity monitor / project activity listings (🗃️) (#7718)
1 parent 9188df0 commit 066b8a2

File tree

19 files changed

+229
-302
lines changed

19 files changed

+229
-302
lines changed

api/specs/web-server/_computations.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66
from models_library.api_schemas_webserver.computations import (
77
ComputationGet,
88
ComputationPathParams,
9+
ComputationRunIterationsLatestListQueryParams,
10+
ComputationRunIterationsListQueryParams,
911
ComputationRunPathParams,
1012
ComputationRunRestGet,
11-
ComputationRunWithFiltersListQueryParams,
1213
ComputationStart,
1314
ComputationStarted,
1415
ComputationTaskRestGet,
1516
)
1617
from models_library.generics import Envelope
1718
from simcore_service_webserver._meta import API_VTAG
1819
from simcore_service_webserver.director_v2._controller.computations_rest import (
19-
ComputationRunListQueryParams,
2020
ComputationTaskListQueryParams,
2121
ComputationTaskPathParams,
2222
)
@@ -71,7 +71,9 @@ async def stop_computation(_path: Annotated[ComputationPathParams, Depends()]):
7171
response_model=Page[ComputationRunRestGet],
7272
)
7373
async def list_computations_latest_iteration(
74-
_query: Annotated[as_query(ComputationRunWithFiltersListQueryParams), Depends()],
74+
_query: Annotated[
75+
as_query(ComputationRunIterationsLatestListQueryParams), Depends()
76+
],
7577
): ...
7678

7779

@@ -80,7 +82,7 @@ async def list_computations_latest_iteration(
8082
response_model=Page[ComputationRunRestGet],
8183
)
8284
async def list_computation_iterations(
83-
_query: Annotated[as_query(ComputationRunListQueryParams), Depends()],
85+
_query: Annotated[as_query(ComputationRunIterationsListQueryParams), Depends()],
8486
_path: Annotated[ComputationRunPathParams, Depends()],
8587
): ...
8688

packages/models-library/src/models_library/api_schemas_webserver/computations.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,20 @@ class ComputationRunListQueryParams(
8484
): ...
8585

8686

87-
class ComputationRunWithFiltersListQueryParams(ComputationRunListQueryParams):
87+
class ComputationRunIterationsLatestListQueryParams(ComputationRunListQueryParams):
8888
filter_only_running: bool = Field(
8989
default=False,
9090
description="If true, only running computations are returned",
9191
)
9292

9393

94+
class ComputationRunIterationsListQueryParams(ComputationRunListQueryParams):
95+
include_children: bool = Field(
96+
default=False,
97+
description="If true, all computational runs of the project and its children are returned (Currently supported only for root projects)",
98+
)
99+
100+
94101
class ComputationRunRestGet(OutputSchema):
95102
project_uuid: ProjectID
96103
iteration: int
@@ -128,7 +135,11 @@ class ComputationTaskPathParams(BaseModel):
128135
class ComputationTaskListQueryParams(
129136
PageQueryParameters,
130137
ComputationTaskListOrderParams, # type: ignore[misc, valid-type]
131-
): ...
138+
):
139+
include_children: bool = Field(
140+
default=False,
141+
description="If true, all tasks of the project and its children are returned (Currently supported only for root projects)",
142+
)
132143

133144

134145
class ComputationTaskRestGet(OutputSchema):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""add index to projects_metadata
2+
3+
Revision ID: 4e7d8719855b
4+
Revises: ba9c4816a31b
5+
Create Date: 2025-05-21 11:48:34.062860+00:00
6+
7+
"""
8+
9+
from alembic import op
10+
11+
# revision identifiers, used by Alembic.
12+
revision = "4e7d8719855b"
13+
down_revision = "ba9c4816a31b"
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
# ### commands auto generated by Alembic - please adjust! ###
20+
op.create_index(
21+
"idx_projects_metadata_root_parent_project_uuid",
22+
"projects_metadata",
23+
["root_parent_project_uuid"],
24+
unique=False,
25+
)
26+
# ### end Alembic commands ###
27+
28+
29+
def downgrade():
30+
# ### commands auto generated by Alembic - please adjust! ###
31+
op.drop_index(
32+
"idx_projects_metadata_root_parent_project_uuid", table_name="projects_metadata"
33+
)
34+
# ### end Alembic commands ###

packages/postgres-database/src/simcore_postgres_database/models/projects_metadata.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
2-
These tables were designed to be controled by projects-plugin in
3-
the webserver's service
2+
These tables were designed to be controled by projects-plugin in
3+
the webserver's service
44
"""
55

66
import sqlalchemy as sa
@@ -100,6 +100,10 @@
100100
ondelete=RefActions.SET_NULL,
101101
name="fk_projects_metadata_root_parent_node_id",
102102
),
103+
#######
104+
sa.Index(
105+
"idx_projects_metadata_root_parent_project_uuid", "root_parent_project_uuid"
106+
),
103107
)
104108

105109

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/director_v2/computations.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ async def list_computations_iterations_page(
6464
*,
6565
product_name: ProductName,
6666
user_id: UserID,
67-
project_id: ProjectID,
67+
project_ids: list[ProjectID],
6868
# pagination
6969
offset: int = 0,
7070
limit: int = 20,
@@ -76,7 +76,7 @@ async def list_computations_iterations_page(
7676
_RPC_METHOD_NAME_ADAPTER.validate_python("list_computations_iterations_page"),
7777
product_name=product_name,
7878
user_id=user_id,
79-
project_id=project_id,
79+
project_ids=project_ids,
8080
offset=offset,
8181
limit=limit,
8282
order_by=order_by,
@@ -92,7 +92,7 @@ async def list_computations_latest_iteration_tasks_page(
9292
*,
9393
product_name: ProductName,
9494
user_id: UserID,
95-
project_id: ProjectID,
95+
project_ids: list[ProjectID],
9696
# pagination
9797
offset: int = 0,
9898
limit: int = 20,
@@ -106,7 +106,7 @@ async def list_computations_latest_iteration_tasks_page(
106106
),
107107
product_name=product_name,
108108
user_id=user_id,
109-
project_id=project_id,
109+
project_ids=project_ids,
110110
offset=offset,
111111
limit=limit,
112112
order_by=order_by,

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/resource_usage_tracker/errors.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class WalletTransactionError(OsparcErrorMixin, Exception):
3535
msg_template = "{msg}"
3636

3737

38-
class CreditTransactionNotFoundError(OsparcErrorMixin, Exception): ...
38+
class CreditTransactionNotFoundError(OsparcErrorMixin, Exception):
39+
msg_template = "Credit transaction for service run id {service_run_id} not found."
3940

4041

4142
### Pricing Plans Error

services/director-v2/src/simcore_service_director_v2/api/rpc/_computations.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async def list_computations_iterations_page(
5959
*,
6060
product_name: ProductName,
6161
user_id: UserID,
62-
project_id: ProjectID,
62+
project_ids: list[ProjectID],
6363
# pagination
6464
offset: int = 0,
6565
limit: int = 20,
@@ -71,7 +71,7 @@ async def list_computations_iterations_page(
7171
await comp_runs_repo.list_for_user_and_project_all_iterations(
7272
product_name=product_name,
7373
user_id=user_id,
74-
project_id=project_id,
74+
project_ids=project_ids,
7575
offset=offset,
7676
limit=limit,
7777
order_by=order_by,
@@ -84,12 +84,12 @@ async def list_computations_iterations_page(
8484

8585

8686
async def _fetch_task_log(
87-
user_id: UserID, project_id: ProjectID, task: ComputationTaskForRpcDBGet
87+
user_id: UserID, task: ComputationTaskForRpcDBGet
8888
) -> TaskLogFileGet | None:
8989
if not task.state.is_running():
9090
return await dask_utils.get_task_log_file(
9191
user_id=user_id,
92-
project_id=project_id,
92+
project_id=task.project_uuid,
9393
node_id=task.node_id,
9494
)
9595
return None
@@ -101,7 +101,7 @@ async def list_computations_latest_iteration_tasks_page(
101101
*,
102102
product_name: ProductName,
103103
user_id: UserID,
104-
project_id: ProjectID,
104+
project_ids: list[ProjectID],
105105
# pagination
106106
offset: int = 0,
107107
limit: int = 20,
@@ -114,20 +114,30 @@ async def list_computations_latest_iteration_tasks_page(
114114
comp_tasks_repo = CompTasksRepository.instance(db_engine=app.state.engine)
115115
comp_runs_repo = CompRunsRepository.instance(db_engine=app.state.engine)
116116

117-
comp_latest_run = await comp_runs_repo.get(
118-
user_id=user_id, project_id=project_id, iteration=None # Returns last iteration
119-
)
120-
121117
total, comp_tasks = await comp_tasks_repo.list_computational_tasks_rpc_domain(
122-
project_id=project_id,
118+
project_ids=project_ids,
123119
offset=offset,
124120
limit=limit,
125121
order_by=order_by,
126122
)
127123

124+
# Get unique set of all project_uuids from comp_tasks
125+
unique_project_uuids = {task.project_uuid for task in comp_tasks}
126+
127+
# Fetch latest run for each project concurrently
128+
latest_runs = await limited_gather(
129+
*[
130+
comp_runs_repo.get(user_id=user_id, project_id=project_uuid, iteration=None)
131+
for project_uuid in unique_project_uuids
132+
],
133+
limit=20,
134+
)
135+
# Build a dict: project_uuid -> iteration
136+
project_uuid_to_iteration = {run.project_uuid: run.iteration for run in latest_runs}
137+
128138
# Run all log fetches concurrently
129139
log_files = await limited_gather(
130-
*[_fetch_task_log(user_id, project_id, task) for task in comp_tasks],
140+
*[_fetch_task_log(user_id, task) for task in comp_tasks],
131141
limit=20,
132142
)
133143

@@ -142,7 +152,10 @@ async def list_computations_latest_iteration_tasks_page(
142152
ended_at=task.ended_at,
143153
log_download_link=log_file.download_link if log_file else None,
144154
service_run_id=ServiceRunID.get_resource_tracking_run_id_for_computational(
145-
user_id, project_id, task.node_id, comp_latest_run.iteration
155+
user_id,
156+
task.project_uuid,
157+
task.node_id,
158+
project_uuid_to_iteration[task.project_uuid],
146159
),
147160
)
148161
for task, log_file in zip(comp_tasks, log_files, strict=True)

services/director-v2/src/simcore_service_director_v2/modules/db/repositories/comp_runs.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ async def list_for_user_and_project_all_iterations(
295295
*,
296296
product_name: str,
297297
user_id: UserID,
298-
project_id: ProjectID,
298+
project_ids: list[ProjectID],
299299
# pagination
300300
offset: int,
301301
limit: int,
@@ -309,7 +309,11 @@ async def list_for_user_and_project_all_iterations(
309309
*self._COMPUTATION_RUNS_RPC_GET_COLUMNS,
310310
).where(
311311
(comp_runs.c.user_id == user_id)
312-
& (comp_runs.c.project_uuid == f"{project_id}")
312+
& (
313+
comp_runs.c.project_uuid.in_(
314+
[f"{project_id}" for project_id in project_ids]
315+
)
316+
)
313317
& (
314318
comp_runs.c.metadata["product_name"].astext == product_name
315319
) # <-- NOTE: We might create a separate column for this for fast retrieval

services/director-v2/src/simcore_service_director_v2/modules/db/repositories/comp_tasks/_core.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ async def list_computational_tasks(
7878
async def list_computational_tasks_rpc_domain(
7979
self,
8080
*,
81-
project_id: ProjectID,
81+
project_ids: list[ProjectID],
8282
# pagination
8383
offset: int = 0,
8484
limit: int = 20,
@@ -100,7 +100,11 @@ async def list_computational_tasks_rpc_domain(
100100
)
101101
.select_from(comp_tasks)
102102
.where(
103-
(comp_tasks.c.project_id == f"{project_id}")
103+
(
104+
comp_tasks.c.project_id.in_(
105+
[f"{project_id}" for project_id in project_ids]
106+
)
107+
)
104108
& (comp_tasks.c.node_class == NodeClass.COMPUTATIONAL)
105109
)
106110
)

services/director-v2/tests/unit/with_dbs/comp_scheduler/test_api_rpc_computations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ async def test_rpc_list_computation_runs_and_tasks(
9797
# Tasks
9898

9999
output = await rpc_computations.list_computations_latest_iteration_tasks_page(
100-
rpc_client, product_name="osparc", user_id=user["id"], project_id=proj.uuid
100+
rpc_client, product_name="osparc", user_id=user["id"], project_ids=[proj.uuid]
101101
)
102102
assert output
103103
assert output.total == 4
@@ -201,7 +201,7 @@ async def test_rpc_list_computation_runs_history(
201201
)
202202

203203
output = await rpc_computations.list_computations_iterations_page(
204-
rpc_client, product_name="osparc", user_id=user["id"], project_id=proj.uuid
204+
rpc_client, product_name="osparc", user_id=user["id"], project_ids=[proj.uuid]
205205
)
206206
assert output.total == 3
207207
assert isinstance(output, ComputationRunRpcGetPage)

0 commit comments

Comments
 (0)