Skip to content

Commit ba1d76e

Browse files
maintenance: project listing
1 parent a37613b commit ba1d76e

File tree

6 files changed

+60
-66
lines changed

6 files changed

+60
-66
lines changed

services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121
ProjectTemplateType as ProjectTemplateTypeDB,
2222
)
2323
from simcore_postgres_database.webserver_models import ProjectType as ProjectTypeDB
24+
from simcore_service_webserver.users.api import get_user_email_legacy
2425

2526
from ..folders import _folders_repository
2627
from ..workspaces.api import check_user_workspace_access
2728
from . import _projects_service
2829
from ._access_rights_repository import batch_get_project_access_rights
2930
from ._projects_repository import batch_get_trashed_by_primary_gid
30-
from ._projects_repository_legacy import ProjectDBAPI
31+
from ._projects_repository_legacy import ProjectDBAPI, convert_to_schema_names
3132
from .models import ProjectDict, ProjectTypeAPI
3233

3334

@@ -53,7 +54,6 @@ async def _aggregate_data_to_projects_from_other_sources(
5354
app: web.Application,
5455
*,
5556
db_projects: list[ProjectDict],
56-
db_project_types: list[ProjectTypeDB],
5757
user_id: UserID,
5858
) -> list[ProjectDict]:
5959
"""
@@ -79,10 +79,10 @@ async def _aggregate_data_to_projects_from_other_sources(
7979
_projects_service.add_project_states_for_user(
8080
user_id=user_id,
8181
project=prj,
82-
is_template=prj_type == ProjectTypeDB.TEMPLATE,
82+
is_template=prj["type"] == ProjectTypeDB.TEMPLATE,
8383
app=app,
8484
)
85-
for prj, prj_type in zip(db_projects, db_project_types, strict=False)
85+
for prj in db_projects
8686
]
8787

8888
updated_projects: list[ProjectDict] = await _paralell_update(
@@ -140,7 +140,7 @@ async def list_projects( # pylint: disable=too-many-arguments
140140
workspace_id=workspace_id,
141141
)
142142

143-
db_projects, db_project_types, total_number_projects = await db.list_projects_dicts(
143+
db_projects, total_number_projects = await db.list_projects_dicts(
144144
product_name=product_name,
145145
user_id=user_id,
146146
workspace_query=(
@@ -172,11 +172,21 @@ async def list_projects( # pylint: disable=too-many-arguments
172172
order_by=order_by,
173173
)
174174

175-
projects = await _aggregate_data_to_projects_from_other_sources(
176-
app, db_projects=db_projects, db_project_types=db_project_types, user_id=user_id
175+
# This is a legacy postprocessing step to convert db schema to API schema (to be backwards compatible)
176+
api_projects: list[dict] = []
177+
178+
for db_prj in db_projects:
179+
db_prj_dict = db_prj.model_dump()
180+
db_prj_dict.pop("product_name", None)
181+
db_prj_dict["tags"] = await db.get_tags_by_project(project_id=f"{db_prj.id}")
182+
user_email = await get_user_email_legacy(app, db_prj.prj_owner)
183+
api_projects.append(convert_to_schema_names(db_prj_dict, user_email))
184+
185+
final_projects = await _aggregate_data_to_projects_from_other_sources(
186+
app, db_projects=api_projects, user_id=user_id
177187
)
178188

179-
return projects, total_number_projects
189+
return final_projects, total_number_projects
180190

181191

182192
async def list_projects_full_depth(
@@ -196,7 +206,7 @@ async def list_projects_full_depth(
196206
) -> tuple[list[ProjectDict], int]:
197207
db = ProjectDBAPI.get_from_app_context(app)
198208

199-
db_projects, db_project_types, total_number_projects = await db.list_projects_dicts(
209+
db_projects, total_number_projects = await db.list_projects_dicts(
200210
product_name=product_name,
201211
user_id=user_id,
202212
workspace_query=WorkspaceQuery(workspace_scope=WorkspaceScope.ALL),
@@ -211,7 +221,7 @@ async def list_projects_full_depth(
211221
)
212222

213223
projects = await _aggregate_data_to_projects_from_other_sources(
214-
app, db_projects=db_projects, db_project_types=db_project_types, user_id=user_id
224+
app, db_projects=db_projects, user_id=user_id
215225
)
216226

217227
return projects, total_number_projects

services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from models_library.folders import FolderQuery, FolderScope
2020
from models_library.groups import GroupID
2121
from models_library.products import ProductName
22-
from models_library.projects import ProjectID, ProjectIDStr
22+
from models_library.projects import ProjectAtDB, ProjectID, ProjectIDStr
2323
from models_library.projects_comments import CommentID, ProjectsCommentsDB
2424
from models_library.projects_nodes import Node
2525
from models_library.projects_nodes_io import NodeID, NodeIDStr
@@ -584,7 +584,7 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
584584
limit: int | None = None,
585585
# order
586586
order_by: OrderBy = DEFAULT_ORDER_BY,
587-
) -> tuple[list[ProjectDict], list[ProjectType], int]:
587+
) -> tuple[list[ProjectAtDB], int]:
588588
async with self.engine.acquire() as conn:
589589
user_groups_proxy: list[RowProxy] = await self._list_user_groups(
590590
conn, user_id
@@ -667,14 +667,15 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
667667
projects.c.id,
668668
)
669669

670-
prjs, prj_types = await self._execute_without_permission_check(
671-
conn,
672-
select_projects_query=combined_query.offset(offset).limit(limit),
673-
)
670+
prjs_at_db = [
671+
ProjectAtDB.model_validate(row)
672+
async for row in conn.execute(
673+
combined_query.offset(offset).limit(limit)
674+
)
675+
]
674676

675677
return (
676-
prjs,
677-
prj_types,
678+
prjs_at_db,
678679
cast(int, total_count),
679680
)
680681

@@ -1249,6 +1250,13 @@ async def remove_tag(
12491250
project["tags"].remove(tag_id)
12501251
return convert_to_schema_names(project, user_email)
12511252

1253+
async def get_tags_by_project(self, project_id: str) -> list:
1254+
async with self.engine.acquire() as conn:
1255+
query = sa.select(projects_tags.c.tag_id).where(
1256+
projects_tags.c.project_id == project_id
1257+
)
1258+
return [row.tag_id async for row in conn.execute(query)]
1259+
12521260
#
12531261
# Project Comments
12541262
#

services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy_utils.py

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import asyncio
21
import logging
32
from collections.abc import Mapping
43
from copy import deepcopy
@@ -9,23 +8,21 @@
98
import sqlalchemy as sa
109
from aiopg.sa.connection import SAConnection
1110
from aiopg.sa.result import RowProxy
12-
from models_library.projects import ProjectAtDB, ProjectID, ProjectTemplateType
11+
from models_library.projects import ProjectID, ProjectTemplateType
1312
from models_library.projects_nodes import Node
1413
from models_library.projects_nodes_io import NodeIDStr
1514
from models_library.utils.change_case import camel_to_snake, snake_to_camel
1615
from pydantic import ValidationError
1716
from simcore_postgres_database.models.project_to_groups import project_to_groups
1817
from simcore_postgres_database.webserver_models import ProjectType, projects
1918
from sqlalchemy.dialects.postgresql import insert as pg_insert
20-
from sqlalchemy.sql.selectable import CompoundSelect, Select
2119

2220
from ..db.models import GroupType, groups, projects_tags, user_to_groups, users
2321
from ..users.exceptions import UserNotFoundError
2422
from ..utils import format_datetime
2523
from ._projects_repository import PROJECT_DB_COLS
2624
from .exceptions import (
2725
NodeNotFoundError,
28-
ProjectInvalidRightsError,
2926
ProjectInvalidUsageError,
3027
ProjectNotFoundError,
3128
)
@@ -184,50 +181,6 @@ async def _upsert_tags_in_project(
184181
.on_conflict_do_nothing()
185182
)
186183

187-
async def _execute_without_permission_check(
188-
self,
189-
conn: SAConnection,
190-
*,
191-
select_projects_query: Select | CompoundSelect,
192-
) -> tuple[list[dict[str, Any]], list[ProjectType]]:
193-
api_projects: list[dict] = [] # API model-compatible projects
194-
db_projects: list[dict] = [] # DB model-compatible projects
195-
project_types: list[ProjectType] = []
196-
async for row in conn.execute(select_projects_query):
197-
assert isinstance(row, RowProxy) # nosec
198-
try:
199-
await asyncio.get_event_loop().run_in_executor(
200-
None, ProjectAtDB.model_validate, row
201-
)
202-
203-
except ProjectInvalidRightsError:
204-
continue
205-
206-
except ValidationError as exc:
207-
logger.warning(
208-
"project %s failed validation, please check. error: %s",
209-
f"{row.id=}",
210-
exc,
211-
)
212-
continue
213-
214-
prj: dict[str, Any] = dict(row.items())
215-
prj.pop("product_name", None)
216-
217-
db_projects.append(prj)
218-
219-
# NOTE: DO NOT nest _get_tags_by_project in async loop above !!!
220-
# FIXME: temporary avoids inner async loops issue https://github.com/aio-libs/aiopg/issues/535
221-
for db_prj in db_projects:
222-
db_prj["tags"] = await self._get_tags_by_project(
223-
conn, project_id=db_prj["id"]
224-
)
225-
user_email = await self._get_user_email(conn, db_prj["prj_owner"])
226-
api_projects.append(convert_to_schema_names(db_prj, user_email))
227-
project_types.append(db_prj["type"])
228-
229-
return (api_projects, project_types)
230-
231184
async def _get_project(
232185
self,
233186
connection: SAConnection,

services/web/server/src/simcore_service_webserver/users/_users_repository.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,18 @@ async def get_user_id_from_pgid(app: web.Application, *, primary_gid: int) -> Us
199199
return user_id
200200

201201

202+
async def get_user_email_legacy(engine: AsyncEngine, *, user_id: UserID | None) -> str:
203+
if not user_id:
204+
205+
async with pass_or_acquire_connection(engine=engine) as conn:
206+
email: str | None = await conn.scalar(
207+
sa.select(
208+
users.c.email,
209+
).where(users.c.id == user_id)
210+
)
211+
return email or "Unknown"
212+
213+
202214
async def get_user_fullname(app: web.Application, *, user_id: UserID) -> FullNameDict:
203215
"""
204216
:raises UserNotFoundError:

services/web/server/src/simcore_service_webserver/users/_users_service.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,15 @@ async def get_user(app: web.Application, user_id: UserID) -> dict[str, Any]:
124124
)
125125

126126

127+
async def get_user_email_legacy(app: web.Application, user_id: UserID | None) -> str:
128+
"""
129+
:raises UserNotFoundError: if missing but NOT if marked for deletion!
130+
"""
131+
return await _users_repository.get_user_email_legacy(
132+
engine=get_asyncpg_engine(app), user_id=user_id
133+
)
134+
135+
127136
async def get_user_primary_group_id(app: web.Application, user_id: UserID) -> GroupID:
128137
return await _users_repository.get_user_primary_group_id(
129138
engine=get_asyncpg_engine(app), user_id=user_id

services/web/server/src/simcore_service_webserver/users/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
get_user,
88
get_user_credentials,
99
get_user_display_and_id_names,
10+
get_user_email_legacy,
1011
get_user_fullname,
1112
get_user_id_from_gid,
1213
get_user_invoice_address,
@@ -28,6 +29,7 @@
2829
"get_user",
2930
"get_user_credentials",
3031
"get_user_display_and_id_names",
32+
"get_user_email_legacy",
3133
"get_user_fullname",
3234
"get_user_id_from_gid",
3335
"get_user_invoice_address",

0 commit comments

Comments
 (0)