11import logging
2- from typing import Any
2+ from typing import Annotated , Any
33
44from aiohttp import web
5+ from annotated_types import doc
56from common_library .users_enums import AccountRequestStatus
67from models_library .api_schemas_webserver .users import UserAccountGet
78from models_library .emails import LowerCaseEmailStr
9+ from models_library .list_operations import OrderDirection
810from models_library .products import ProductName
911from models_library .users import UserID
1012from notifications_library ._email import create_email_session
1113from pydantic import HttpUrl
1214from settings_library .email import SMTPSettings
13- from simcore_service_webserver .products ._service import get_product
1415
1516from ..db .plugin import get_asyncpg_engine
17+ from ..products ._service import get_product
1618from . import _accounts_repository , _users_repository
19+ from ._accounts_repository import OrderKeys
1720from .exceptions import (
1821 AlreadyPreRegisteredError ,
1922 PendingPreRegistrationNotFoundError ,
@@ -31,7 +34,10 @@ async def pre_register_user(
3134 app : web .Application ,
3235 * ,
3336 profile : UserAccountRestPreRegister ,
34- creator_user_id : UserID | None ,
37+ creator_user_id : Annotated [
38+ UserID | None ,
39+ doc ("ID of the user creating the pre-registration (None for anonymous)" ),
40+ ],
3541 product_name : ProductName ,
3642) -> UserAccountGet :
3743
@@ -92,19 +98,23 @@ async def list_user_accounts(
9298 app : web .Application ,
9399 * ,
94100 product_name : ProductName ,
95- filter_any_account_request_status : list [AccountRequestStatus ] | None = None ,
101+ filter_any_account_request_status : Annotated [
102+ list [AccountRequestStatus ] | None ,
103+ doc ("List of any account request statuses to filter by" ),
104+ ] = None ,
96105 pagination_limit : int = 50 ,
97106 pagination_offset : int = 0 ,
98- ) -> tuple [list [dict [str , Any ]], int ]:
107+ order_by : Annotated [
108+ list [tuple [OrderKeys , OrderDirection ]] | None ,
109+ doc ("List of (field_name, direction) tuples for ordering" ),
110+ ] = None ,
111+ ) -> Annotated [
112+ tuple [list [dict [str , Any ]], int ],
113+ doc ("Tuple containing (list of user dictionaries, total count of users)" ),
114+ ]:
99115 """
100116 Get a paginated list of users for admin view with filtering options.
101117
102- Args:
103- app: The web application instance
104- filter_any_account_request_status: List of *any* account request statuses to filter by
105- pagination_limit: Maximum number of users to return
106- pagination_offset: Number of users to skip for pagination
107-
108118 Returns:
109119 A tuple containing (list of user dictionaries, total count of users)
110120 """
@@ -118,6 +128,7 @@ async def list_user_accounts(
118128 filter_any_account_request_status = filter_any_account_request_status ,
119129 pagination_limit = pagination_limit ,
120130 pagination_offset = pagination_offset ,
131+ order_by = order_by ,
121132 )
122133 )
123134
@@ -155,8 +166,7 @@ async def search_users_accounts(
155166 product_name : ProductName | None = None ,
156167 include_products : bool = False ,
157168) -> list [UserAccountGet ]:
158- """
159- WARNING: this information is reserved for admin users. Note that the returned model include UserForAdminGet
169+ """WARNING: this information is reserved for admin users. Note that the returned model include UserForAdminGet
160170
161171 NOTE: Functions in the service layer typically validate the caller's access rights
162172 using parameters like product_name and user_id. However, this function skips
@@ -238,18 +248,14 @@ async def approve_user_account(
238248 pre_registration_email : LowerCaseEmailStr ,
239249 product_name : ProductName ,
240250 reviewer_id : UserID ,
241- invitation_extras : dict [str , Any ] | None = None ,
242- ) -> int :
251+ invitation_extras : Annotated [
252+ dict [str , Any ] | None , doc ("Optional invitation data to store in extras field" )
253+ ] = None ,
254+ ) -> Annotated [int , doc ("The ID of the approved pre-registration record" )]:
243255 """Approve a user account based on their pre-registration email.
244256
245- Args:
246- app: The web application instance
247- pre_registration_email: Email of the pre-registered user to approve
248- product_name: Product name for which the user is being approved
249- reviewer_id: ID of the user approving the account
250-
251257 Returns:
252- int: The ID of the approved pre-registration record
258+ The ID of the approved pre-registration record
253259
254260 Raises:
255261 PendingPreRegistrationNotFoundError: If no pre-registration is found for the email/product
@@ -291,17 +297,9 @@ async def reject_user_account(
291297 pre_registration_email : LowerCaseEmailStr ,
292298 product_name : ProductName ,
293299 reviewer_id : UserID ,
294- ) -> int :
300+ ) -> Annotated [ int , doc ( "The ID of the rejected pre-registration record" )] :
295301 """Reject a user account based on their pre-registration email.
296302
297- Args:
298- app: The web application instance
299- pre_registration_email: Email of the pre-registered user to reject
300- product_name: Product name for which the user is being rejected
301- reviewer_id: ID of the user rejecting the account
302-
303- Returns:
304- int: The ID of the rejected pre-registration record
305303
306304 Raises:
307305 PendingPreRegistrationNotFoundError: If no pre-registration is found for the email/product
@@ -343,9 +341,17 @@ def _create_product_and_user_data(
343341 user_email : LowerCaseEmailStr ,
344342 first_name : str ,
345343 last_name : str ,
346- ):
344+ ) -> Annotated [
345+ tuple [Any , Any ],
346+ doc ("Tuple containing (ProductData, UserData) objects for email rendering" ),
347+ ]:
347348 """Create ProductData and UserData objects for email rendering."""
348- from notifications_library ._models import ProductData , ProductUIData , UserData
349+
350+ from notifications_library ._models import ( # noqa: PLC0415
351+ ProductData ,
352+ ProductUIData ,
353+ UserData ,
354+ )
349355
350356 # Get product data from the app
351357 product = get_product (app , product_name = product_name )
@@ -399,13 +405,13 @@ async def send_approval_email_to_user(
399405 first_name : str ,
400406 last_name : str ,
401407) -> None :
402- from notifications_library ._email import compose_email
403- from notifications_library ._email_render import (
408+ from notifications_library ._email import compose_email # noqa: PLC0415
409+ from notifications_library ._email_render import ( # noqa: PLC0415
404410 get_support_address ,
405411 get_user_address ,
406412 render_email_parts ,
407413 )
408- from notifications_library ._render import (
414+ from notifications_library ._render import ( # noqa: PLC0415
409415 create_render_environment_from_notifications_library ,
410416 )
411417
@@ -456,13 +462,13 @@ async def send_rejection_email_to_user(
456462 last_name : str ,
457463 host : str ,
458464) -> None :
459- from notifications_library ._email import compose_email
460- from notifications_library ._email_render import (
465+ from notifications_library ._email import compose_email # noqa: PLC0415
466+ from notifications_library ._email_render import ( # noqa: PLC0415
461467 get_support_address ,
462468 get_user_address ,
463469 render_email_parts ,
464470 )
465- from notifications_library ._render import (
471+ from notifications_library ._render import ( # noqa: PLC0415
466472 create_render_environment_from_notifications_library ,
467473 )
468474
0 commit comments