diff --git a/docs/changelog.md b/docs/changelog.md index 38885a4..ccadd1b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format. +## [0.5.4] -- 2023-07-14 +- Added namespaces info method that returns list of namespaces with + ## [0.5.3] -- 2023-07-13 - Fixed bugs in updating dates and descriptions diff --git a/pepdbagent/_version.py b/pepdbagent/_version.py index 43a1e95..6b27eee 100644 --- a/pepdbagent/_version.py +++ b/pepdbagent/_version.py @@ -1 +1 @@ -__version__ = "0.5.3" +__version__ = "0.5.4" diff --git a/pepdbagent/const.py b/pepdbagent/const.py index 185364b..68c8a4f 100644 --- a/pepdbagent/const.py +++ b/pepdbagent/const.py @@ -13,3 +13,5 @@ # db_dialects POSTGRES_DIALECT = "postgresql" + +DEFAULT_LIMIT_INFO = 5 diff --git a/pepdbagent/models.py b/pepdbagent/models.py index d9ad64a..0703940 100644 --- a/pepdbagent/models.py +++ b/pepdbagent/models.py @@ -130,3 +130,22 @@ def value_should_not_contain_question(cls, v): class Config: extra = Extra.forbid allow_population_by_field_name = True + + +class NamespaceInfo(BaseModel): + """ + Model with information about namespace + """ + + namespace: str + number_of_projects: int + + +class ListOfNamespaceInfo(BaseModel): + """ + Namespace information response model + """ + + number_of_namespaces: int + limit: int + results: List[NamespaceInfo] diff --git a/pepdbagent/modules/namespace.py b/pepdbagent/modules/namespace.py index 656ff50..7066650 100644 --- a/pepdbagent/modules/namespace.py +++ b/pepdbagent/modules/namespace.py @@ -1,13 +1,13 @@ import logging from typing import List, Union -from sqlalchemy import distinct, func, or_, select +from sqlalchemy import distinct, func, or_, select, text from sqlalchemy.sql.selectable import Select from sqlalchemy.orm import Session -from pepdbagent.const import DEFAULT_LIMIT, DEFAULT_OFFSET, PKG_NAME +from pepdbagent.const import DEFAULT_LIMIT, DEFAULT_OFFSET, PKG_NAME, DEFAULT_LIMIT_INFO from pepdbagent.db_utils import Projects, BaseEngine -from pepdbagent.models import Namespace, NamespaceList +from pepdbagent.models import Namespace, NamespaceList, NamespaceInfo, ListOfNamespaceInfo from pepdbagent.utils import tuple_converter _LOGGER = logging.getLogger(PKG_NAME) @@ -71,6 +71,7 @@ def _get_namespace( ) -> List[Namespace]: """ Search for namespace by providing search string. + :param search_str: string of symbols, words, keywords to search in the namespace name. :param admin_nsp: tuple of namespaces where project can be retrieved if they are privet @@ -114,9 +115,10 @@ def _get_namespace( ) return results_list - def _count_namespace(self, search_str: str = None, admin_nsp: tuple = None) -> int: + def _count_namespace(self, search_str: str = None, admin_nsp: tuple = tuple()) -> int: """ Get number of found namespace. [This function is related to _get_namespaces] + :param search_str: string of symbols, words, keywords to search in the namespace name. :param admin_nsp: tuple of namespaces where project can be retrieved if they are privet @@ -160,3 +162,45 @@ def _add_condition( or_(Projects.private.is_(False), Projects.namespace.in_(admin_list)) ) return statement + + def info(self, limit: int = DEFAULT_LIMIT_INFO) -> ListOfNamespaceInfo: + """ + Get list of top n namespaces in the database + + :param limit: limit of results (top namespace ) + :return: number_of_namespaces: int + limit: int + results: { namespace: str + number_of_projects: int + } + """ + total_number_of_namespaces = self._count_namespace() + + statement = ( + select( + func.count(Projects.namespace).label("number_of_projects"), + Projects.namespace, + ) + .select_from(Projects) + .where(Projects.private.is_(False)) + .limit(limit) + .order_by(text("number_of_projects desc")) + .group_by(Projects.namespace) + ) + + with Session(self._sa_engine) as session: + query_results = session.execute(statement).all() + + list_of_results = [] + for result in query_results: + list_of_results.append( + NamespaceInfo( + namespace=result.namespace, + number_of_projects=result.number_of_projects, + ) + ) + return ListOfNamespaceInfo( + number_of_namespaces=total_number_of_namespaces, + limit=limit, + results=list_of_results, + ) diff --git a/pepdbagent/modules/project.py b/pepdbagent/modules/project.py index 3ccbeb5..6bb8b05 100644 --- a/pepdbagent/modules/project.py +++ b/pepdbagent/modules/project.py @@ -350,11 +350,12 @@ def _overwrite( found_prj.pep_schema = pep_schema found_prj.config = project_dict[CONFIG_KEY] found_prj.description = description + found_prj.last_update_date = datetime.datetime.now(datetime.timezone.utc) # Deleting old samples and subsamples if found_prj.samples_mapping: for sample in found_prj.samples_mapping: - _LOGGER.info(f"deleting samples: {str(sample)}") + _LOGGER.debug(f"deleting samples: {str(sample)}") session.delete(sample) if found_prj.subsamples_mapping: diff --git a/tests/test_pepagent.py b/tests/test_pepagent.py index 96c6618..518e08e 100644 --- a/tests/test_pepagent.py +++ b/tests/test_pepagent.py @@ -410,3 +410,14 @@ def test_annotation(self, initiate_pepdb_con): def test_annotation_private(self, initiate_pepdb_con): result = initiate_pepdb_con.namespace.get(admin="private_test") assert len(result.results) == 4 + + def test_namespace_info(self, initiate_pepdb_con): + initiate_pepdb_con.project.update( + namespace="private_test", + name="derive", + tag="default", + update_dict={"is_private": False}, + ) + result = initiate_pepdb_con.namespace.info() + assert len(result.results) == 4 + assert result.results[3].number_of_projects == 1