diff --git a/antarest/study/service.py b/antarest/study/service.py index 7290cf692d..5433aab1c2 100644 --- a/antarest/study/service.py +++ b/antarest/study/service.py @@ -491,6 +491,22 @@ def get_studies_information( ) } + def count_studies( + self, + study_filter: StudyFilter, + ) -> int: + """ + Get number of matching studies. + Args: + study_filter: filtering parameters + + Returns: total number of studies matching the filtering criteria + """ + total: int = self.repository.count_studies( + study_filter=study_filter, + ) + return total + def _try_get_studies_information(self, study: Study) -> t.Optional[StudyMetadataDTO]: try: return self.storage_service.get_storage(study).get_study_information(study) diff --git a/antarest/study/web/studies_blueprint.py b/antarest/study/web/studies_blueprint.py index cf76765122..01da60a083 100644 --- a/antarest/study/web/studies_blueprint.py +++ b/antarest/study/web/studies_blueprint.py @@ -34,6 +34,8 @@ logger = logging.getLogger(__name__) +QUERY_REGEX = r"^\s*(?:\d+\s*(?:,\s*\d+\s*)*)?$" + def _split_comma_separated_values(value: str, *, default: t.Sequence[str] = ()) -> t.Sequence[str]: """Split a comma-separated list of values into an ordered set of strings.""" @@ -76,23 +78,11 @@ def get_studies( managed: t.Optional[bool] = Query(None, description="Filter studies based on their management status."), archived: t.Optional[bool] = Query(None, description="Filter studies based on their archive status."), variant: t.Optional[bool] = Query(None, description="Filter studies based on their variant status."), - versions: str = Query( - "", - description="Comma-separated list of versions for filtering.", - regex=r"^\s*(?:\d+\s*(?:,\s*\d+\s*)*)?$", - ), - users: str = Query( - "", - description="Comma-separated list of user IDs for filtering.", - regex=r"^\s*(?:\d+\s*(?:,\s*\d+\s*)*)?$", - ), + versions: str = Query("", description="Comma-separated list of versions for filtering.", regex=QUERY_REGEX), + users: str = Query("", description="Comma-separated list of user IDs for filtering.", regex=QUERY_REGEX), groups: str = Query("", description="Comma-separated list of group IDs for filtering."), tags: str = Query("", description="Comma-separated list of tags for filtering."), - study_ids: str = Query( - "", - description="Comma-separated list of study IDs for filtering.", - alias="studyIds", - ), + study_ids: str = Query("", description="Comma-separated list of study IDs for filtering.", alias="studyIds"), exists: t.Optional[bool] = Query(None, description="Filter studies based on their existence on disk."), workspace: str = Query("", description="Filter studies based on their workspace."), folder: str = Query("", description="Filter studies based on their folder."), @@ -102,23 +92,17 @@ def get_studies( description="Sort studies based on their name (case-insensitive) or creation date.", alias="sortBy", ), - page_nb: NonNegativeInt = Query( - 0, - description="Page number (starting from 0).", - alias="pageNb", - ), + page_nb: NonNegativeInt = Query(0, description="Page number (starting from 0).", alias="pageNb"), page_size: NonNegativeInt = Query( - 0, - description="Number of studies per page (0 = no limit).", - alias="pageSize", + 0, description="Number of studies per page (0 = no limit).", alias="pageSize" ), ) -> t.Dict[str, StudyMetadataDTO]: """ Get the list of studies matching the specified criteria. Args: + - `name`: Filter studies based on their name. Case-insensitive search for studies - whose name contains the specified value. - `managed`: Filter studies based on their management status. - `archived`: Filter studies based on their archive status. - `variant`: Filter studies based on their variant status. @@ -173,6 +157,77 @@ def get_studies( return matching_studies + @bp.get( + "/studies/count", + tags=[APITag.study_management], + summary="Count Studies", + ) + def count_studies( + current_user: JWTUser = Depends(auth.get_current_user), + name: str = Query("", description="Case-insensitive: filter studies based on their name.", alias="name"), + managed: t.Optional[bool] = Query(None, description="Management status filter."), + archived: t.Optional[bool] = Query(None, description="Archive status filter."), + variant: t.Optional[bool] = Query(None, description="Variant status filter."), + versions: str = Query("", description="Comma-separated versions filter.", regex=QUERY_REGEX), + users: str = Query("", description="Comma-separated user IDs filter.", regex=QUERY_REGEX), + groups: str = Query("", description="Comma-separated group IDs filter."), + tags: str = Query("", description="Comma-separated tags filter."), + study_ids: str = Query("", description="Comma-separated study IDs filter.", alias="studyIds"), + exists: t.Optional[bool] = Query(None, description="Existence on disk filter."), + workspace: str = Query("", description="Workspace filter."), + folder: str = Query("", description="Study folder filter."), + ) -> int: + """ + Get the number of studies matching the specified criteria. + + Args: + + - `name`: Regexp to filter through studies based on their names + - `managed`: Whether to limit the selection based on management status. + - `archived`: Whether to limit the selection based on archive status. + - `variant`: Whether to limit the selection either raw or variant studies. + - `versions`: Comma-separated versions for studies to be selected. + - `users`: Comma-separated user IDs for studies to be selected. + - `groups`: Comma-separated group IDs for studies to be selected. + - `tags`: Comma-separated tags for studies to be selected. + - `studyIds`: Comma-separated IDs of studies to be selected. + - `exists`: Whether to limit the selection based on studies' existence on disk. + - `workspace`: to limit studies selection based on their workspace. + - `folder`: to limit studies selection based on their folder. + + Returns: + - An integer representing the total number of studies matching the filters above and the user permissions. + """ + + logger.info("Counting matching studies", extra={"user": current_user.id}) + params = RequestParameters(user=current_user) + + user_list = [int(v) for v in _split_comma_separated_values(users)] + + if not params.user: + logger.error("FAIL permission: user is not logged") + raise UserHasNotPermissionError() + + total_matching_studies = study_service.count_studies( + study_filter=StudyFilter( + name=name, + managed=managed, + archived=archived, + variant=variant, + versions=_split_comma_separated_values(versions), + users=user_list, + groups=_split_comma_separated_values(groups), + tags=_split_comma_separated_values(tags), + study_ids=_split_comma_separated_values(study_ids), + exists=exists, + workspace=workspace, + folder=folder, + access_permissions=AccessPermissions.from_params(params), + ), + ) + + return total_matching_studies + @bp.get( "/studies/{uuid}/comments", tags=[APITag.study_management],