Skip to content

🎨 Improve listing task manager (add filter + and project name, project custom metadata fields) #7661

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9442bfd
fix
matusdrobuliak66 May 6, 2025
44e5a5c
fix
matusdrobuliak66 May 6, 2025
547cbcb
review @sanderegg
matusdrobuliak66 May 6, 2025
9f99c9c
Merge branch 'master' into improve-listing-task-manager-2
matusdrobuliak66 May 8, 2025
42500d9
improve listing task manager
matusdrobuliak66 May 12, 2025
29edb1b
Merge branch 'master' into improve-listing-task-manager-2
matusdrobuliak66 May 12, 2025
bb8a6c2
fix:
matusdrobuliak66 May 12, 2025
86019e4
adding unit test
matusdrobuliak66 May 12, 2025
e76f5e7
improve comment
matusdrobuliak66 May 12, 2025
7ea3e4c
return back to previous
matusdrobuliak66 May 12, 2025
cb94158
Merge branch 'master' into improve-listing-task-manager-2
matusdrobuliak66 May 12, 2025
c717a2b
fix tests
matusdrobuliak66 May 12, 2025
0bf9c95
Merge branch 'master' into improve-listing-task-manager-2
matusdrobuliak66 May 12, 2025
b724757
review @sanderegg
matusdrobuliak66 May 12, 2025
b032154
Merge branch 'master' into improve-listing-task-manager-2
matusdrobuliak66 May 12, 2025
0b6bd92
Merge branch 'master' into improve-listing-task-manager-3
matusdrobuliak66 May 12, 2025
62e80b7
work
matusdrobuliak66 May 12, 2025
568f7fd
adding unit test
matusdrobuliak66 May 13, 2025
ff15faa
fix
matusdrobuliak66 May 13, 2025
c190854
fix
matusdrobuliak66 May 13, 2025
9aebe95
Merge branch 'master' into improve-listing-task-manager-3
matusdrobuliak66 May 13, 2025
a3316e0
minor small fixes
matusdrobuliak66 May 13, 2025
d9afcf0
Merge branch 'improve-listing-task-manager-3' of github.com:matusdrob…
matusdrobuliak66 May 13, 2025
80de8a1
Merge branch 'master' into improve-listing-task-manager-3
matusdrobuliak66 May 13, 2025
cbefa65
Merge branch 'master' into improve-listing-task-manager-3
matusdrobuliak66 May 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ class ComputationStarted(OutputSchemaWithoutCamelCase):
class ComputationRunListQueryParams(
PageQueryParameters,
ComputationRunListOrderParams, # type: ignore[misc, valid-type]
): ...
):
filter_only_running: bool = Field(
default=False,
description="If true, only running computations are returned",
)


class ComputationRunRestGet(OutputSchema):
Expand All @@ -92,6 +96,8 @@ class ComputationRunRestGet(OutputSchema):
submitted_at: datetime
started_at: datetime | None
ended_at: datetime | None
root_project_name: str
project_custom_metadata: dict[str, Any]


### Computation Task
Expand Down
15 changes: 15 additions & 0 deletions packages/models-library/src/models_library/computations.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from decimal import Decimal
from typing import Any

from models_library.api_schemas_webserver.projects_metadata import MetadataDict
from pydantic import AnyUrl, BaseModel

from .projects import ProjectID
Expand All @@ -22,3 +23,17 @@ class ComputationTaskWithAttributes(BaseModel):
# Attributes added by the webserver
node_name: str
osparc_credits: Decimal | None


class ComputationRunWithAttributes(BaseModel):
project_uuid: ProjectID
iteration: int
state: RunningState
info: dict[str, Any]
submitted_at: datetime
started_at: datetime | None
ended_at: datetime | None

# Attributes added by the webserver
root_project_name: str
project_custom_metadata: MetadataDict
10 changes: 7 additions & 3 deletions packages/models-library/src/models_library/projects_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,18 @@ class RunningState(str, Enum):
ABORTED = "ABORTED"
WAITING_FOR_CLUSTER = "WAITING_FOR_CLUSTER"

def is_running(self) -> bool:
return self in (
@staticmethod
def list_running_states() -> list["RunningState"]:
return [
RunningState.PUBLISHED,
RunningState.PENDING,
RunningState.WAITING_FOR_RESOURCES,
RunningState.STARTED,
RunningState.WAITING_FOR_CLUSTER,
)
]

def is_running(self) -> bool:
return self in self.list_running_states()


@unique
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ async def list_computations_latest_iteration_page(
*,
product_name: ProductName,
user_id: UserID,
# filters
filter_only_running: bool = False,
# pagination
offset: int = 0,
limit: int = 20,
Expand All @@ -46,6 +48,7 @@ async def list_computations_latest_iteration_page(
),
product_name=product_name,
user_id=user_id,
filter_only_running=filter_only_running,
offset=offset,
limit=limit,
order_by=order_by,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ async def list_computations_latest_iteration_page(
*,
product_name: ProductName,
user_id: UserID,
# filters
filter_only_running: bool = False,
# pagination
offset: int = 0,
limit: int = 20,
Expand All @@ -39,6 +41,7 @@ async def list_computations_latest_iteration_page(
await comp_runs_repo.list_for_user__only_latest_iterations(
product_name=product_name,
user_id=user_id,
filter_only_running=filter_only_running,
offset=offset,
limit=limit,
order_by=order_by,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ async def list_for_user__only_latest_iterations(
*,
product_name: str,
user_id: UserID,
# filters
filter_only_running: bool,
# pagination
offset: int,
limit: int,
Expand Down Expand Up @@ -229,6 +231,16 @@ async def list_for_user__only_latest_iterations(
& (
comp_runs.c.metadata["product_name"].astext == product_name
) # <-- NOTE: We might create a separate column for this for fast retrieval
& (
comp_runs.c.result.in_(
[
RUNNING_STATE_TO_DB[item]
for item in RunningState.list_running_states()
]
)
)
if filter_only_running
else True
)
.group_by(comp_runs.c.project_uuid)
.subquery("latest_runs")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2567,6 +2567,13 @@ paths:
type: integer
default: 0
title: Offset
- name: filter_only_running
in: query
required: false
schema:
type: boolean
default: false
title: Filter Only Running
responses:
'200':
description: Successful Response
Expand Down Expand Up @@ -9102,6 +9109,12 @@ components:
format: date-time
- type: 'null'
title: Endedat
rootProjectName:
type: string
title: Rootprojectname
projectCustomMetadata:
type: object
title: Projectcustommetadata
type: object
required:
- projectUuid
Expand All @@ -9111,6 +9124,8 @@ components:
- submittedAt
- startedAt
- endedAt
- rootProjectName
- projectCustomMetadata
title: ComputationRunRestGet
ComputationStart:
properties:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from decimal import Decimal

from aiohttp import web
from models_library.api_schemas_directorv2.comp_runs import (
ComputationRunRpcGetPage,
from models_library.computations import (
ComputationRunWithAttributes,
ComputationTaskWithAttributes,
)
from models_library.computations import ComputationTaskWithAttributes
from models_library.products import ProductName
from models_library.projects import ProjectID
from models_library.rest_ordering import OrderBy
Expand All @@ -17,36 +17,86 @@
from servicelib.utils import limited_gather

from ..products.products_service import is_product_billable
from ..projects.api import check_user_project_permission, get_project_dict_legacy
from ..projects.api import (
batch_get_project_name,
check_user_project_permission,
get_project_dict_legacy,
)
from ..projects.projects_metadata_service import (
get_project_custom_metadata_or_empty_dict,
)
from ..rabbitmq import get_rabbitmq_rpc_client


async def list_computations_latest_iteration(
app: web.Application,
product_name: ProductName,
user_id: UserID,
# filters
filter_only_running: bool, # noqa: FBT001
# pagination
offset: int,
limit: NonNegativeInt,
# ordering
order_by: OrderBy,
) -> ComputationRunRpcGetPage:
) -> tuple[int, list[ComputationRunWithAttributes]]:
"""Returns the list of computations (only latest iterations)"""
rpc_client = get_rabbitmq_rpc_client(app)
_runs_get = await computations.list_computations_latest_iteration_page(
rpc_client,
product_name=product_name,
user_id=user_id,
filter_only_running=filter_only_running,
offset=offset,
limit=limit,
order_by=order_by,
)

# NOTE: MD: Get project metadata
# NOTE: MD: Get Root project name
assert _runs_get # nosec
# Get projects metadata (NOTE: MD: can be improved with a single batch call)
_projects_metadata = await limited_gather(
*[
get_project_custom_metadata_or_empty_dict(
app, project_uuid=item.project_uuid
)
for item in _runs_get.items
],
limit=20,
)

# Get Root project names
_projects_root_uuids: list[ProjectID] = []
for item in _runs_get.items:
if (
name := item.info.get("project_metadata", {}).get(
"root_parent_project_id", None
)
) is not None:
_projects_root_uuids.append(ProjectID(name))
else:
_projects_root_uuids.append(item.project_uuid)

_projects_root_names = await batch_get_project_name(
app, projects_uuids=_projects_root_uuids
)

_computational_runs_output = [
ComputationRunWithAttributes(
project_uuid=item.project_uuid,
iteration=item.iteration,
state=item.state,
info=item.info,
submitted_at=item.submitted_at,
started_at=item.started_at,
ended_at=item.ended_at,
root_project_name=project_name,
project_custom_metadata=project_metadata,
)
for item, project_metadata, project_name in zip(
_runs_get.items, _projects_metadata, _projects_root_names, strict=True
)
]

return _runs_get
return _runs_get.total, _computational_runs_output


async def list_computations_latest_iteration_tasks(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ async def list_computations_latest_iteration(request: web.Request) -> web.Respon
ComputationRunListQueryParams, request
)

_get = await _computations_service.list_computations_latest_iteration(
total, items = await _computations_service.list_computations_latest_iteration(
request.app,
product_name=req_ctx.product_name,
user_id=req_ctx.user_id,
# filters
filter_only_running=query_params.filter_only_running,
# pagination
offset=query_params.offset,
limit=query_params.limit,
Expand All @@ -67,10 +69,10 @@ async def list_computations_latest_iteration(request: web.Request) -> web.Respon
page = Page[ComputationRunRestGet].model_validate(
paginate_data(
chunk=[
ComputationRunRestGet.model_validate(task, from_attributes=True)
for task in _get.items
ComputationRunRestGet.model_validate(run, from_attributes=True)
for run in items
],
total=_get.total,
total=total,
limit=query_params.limit,
offset=query_params.offset,
request_url=request.url,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async def get_project_metadata(request: web.Request) -> web.Response:
req_ctx = RequestContext.model_validate(request)
path_params = parse_request_path_parameters_as(ProjectPathParams, request)

custom_metadata = await _metadata_service.get_project_custom_metadata(
custom_metadata = await _metadata_service.get_project_custom_metadata_for_user(
request.app, user_id=req_ctx.user_id, project_uuid=path_params.project_id
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
from ..db.plugin import get_database_engine
from . import _metadata_repository
from ._access_rights_service import validate_project_ownership
from .exceptions import ProjectNotFoundError

_logger = logging.getLogger(__name__)


async def get_project_custom_metadata(
async def get_project_custom_metadata_for_user(
app: web.Application, user_id: UserID, project_uuid: ProjectID
) -> MetadataDict:
await validate_project_ownership(app, user_id=user_id, project_uuid=project_uuid)
Expand All @@ -25,6 +26,29 @@ async def get_project_custom_metadata(
)


async def get_project_custom_metadata(
app: web.Application, project_uuid: ProjectID
) -> MetadataDict:
"""Can Raise ProjectNotFoundError"""
return await _metadata_repository.get_project_custom_metadata(
engine=get_database_engine(app), project_uuid=project_uuid
)


async def get_project_custom_metadata_or_empty_dict(
app: web.Application, project_uuid: ProjectID
) -> MetadataDict:
try:
output = await _metadata_repository.get_project_custom_metadata(
engine=get_database_engine(app), project_uuid=project_uuid
)
except ProjectNotFoundError:
# This is a valid case when the project is not found
# but we still want to return an empty dict
output = {}
return output


async def set_project_custom_metadata(
app: web.Application,
user_id: UserID,
Expand Down
Loading
Loading