11import logging
2+ from typing import cast
23
4+ import sqlalchemy as sa
5+ from models_library .products import ProductName
36from models_library .projects import ProjectID
7+ from models_library .users import UserID
8+ from pydantic import TypeAdapter
9+ from simcore_postgres_database .models .groups import user_to_groups
10+ from simcore_postgres_database .models .project_to_groups import project_to_groups
11+ from simcore_postgres_database .models .projects import projects
412from simcore_postgres_database .models .projects_to_jobs import projects_to_jobs
5- from simcore_postgres_database .utils_repos import transaction_context
13+ from simcore_postgres_database .models .projects_to_products import projects_to_products
14+ from simcore_postgres_database .utils_repos import (
15+ get_columns_from_db_model ,
16+ pass_or_acquire_connection ,
17+ transaction_context ,
18+ )
619from sqlalchemy .dialects .postgresql import insert as pg_insert
720from sqlalchemy .ext .asyncio import AsyncConnection
821
922from ..db .base_repository import BaseRepository
23+ from .models import ProjectDBGet , ProjectJobDBGet
1024
1125_logger = logging .getLogger (__name__ )
1226
1327
28+ _PROJECT_JOB_DB_COLS = [
29+ * get_columns_from_db_model (
30+ projects ,
31+ ProjectDBGet ,
32+ ),
33+ projects_to_jobs .c .job_parent_resource_name , # Add job_parent_resource_name
34+ ]
35+
36+
1437class ProjectJobsRepository (BaseRepository ):
1538
1639 async def set_project_as_job (
@@ -34,3 +57,87 @@ async def set_project_as_job(
3457 )
3558
3659 await conn .execute (stmt )
60+
61+ async def list_projects_marked_as_jobs (
62+ self ,
63+ connection : AsyncConnection | None = None ,
64+ * ,
65+ product_name : ProductName ,
66+ user_id : UserID ,
67+ offset : int = 0 ,
68+ limit : int = 10 ,
69+ job_parent_resource_name_filter : str | None = None ,
70+ ) -> tuple [int , list [ProjectJobDBGet ]]:
71+ """
72+ Lists projects marked as jobs for a specific user and product
73+ """
74+
75+ # Step 1: Get group IDs associated with the user
76+ user_groups_query = (
77+ sa .select (user_to_groups .c .gid )
78+ .where (user_to_groups .c .uid == user_id )
79+ .subquery ()
80+ )
81+
82+ # Step 2: Create access_query to filter projects based on product_name and read access
83+ access_query = (
84+ sa .select (projects_to_jobs .c .project_uuid )
85+ .select_from (
86+ projects_to_jobs .join (
87+ projects_to_products ,
88+ projects_to_jobs .c .project_uuid
89+ == projects_to_products .c .project_uuid ,
90+ ).join (
91+ project_to_groups ,
92+ projects_to_jobs .c .project_uuid == project_to_groups .c .project_uuid ,
93+ )
94+ )
95+ .where (
96+ projects_to_products .c .product_name == product_name ,
97+ project_to_groups .c .gid .in_ (sa .select (user_groups_query .c .gid )),
98+ project_to_groups .c .read .is_ (True ),
99+ )
100+ )
101+
102+ # Apply job_parent_resource_name_filter if provided
103+ if job_parent_resource_name_filter :
104+ access_query = access_query .where (
105+ projects_to_jobs .c .job_parent_resource_name .like (
106+ f"%{ job_parent_resource_name_filter } %"
107+ )
108+ )
109+
110+ # Convert access_query to a subquery
111+ base_query = access_query .subquery ()
112+
113+ # Step 3: Query to get the total count
114+ total_query = sa .select (sa .func .count ()).select_from (base_query )
115+
116+ # Step 4: Query to get the paginated list with full selection
117+ list_query = (
118+ sa .select (* _PROJECT_JOB_DB_COLS )
119+ .select_from (
120+ base_query .join (
121+ projects ,
122+ projects .c .uuid == base_query .c .project_uuid ,
123+ )
124+ )
125+ .order_by (
126+ projects .c .creation_date .desc (), # latests first
127+ projects .c .id .desc (),
128+ )
129+ .limit (limit )
130+ .offset (offset )
131+ )
132+
133+ # Step 5: Execute queries
134+ async with pass_or_acquire_connection (self .engine , connection ) as conn :
135+ total_count = await conn .scalar (total_query )
136+ assert isinstance (total_count , int ) # nosec
137+
138+ result = await conn .execute (list_query )
139+ projects_list = TypeAdapter (list [ProjectJobDBGet ]).validate_python (
140+ result .fetchall ()
141+ )
142+
143+ return cast (int , total_count ), projects_list
0 commit comments