Skip to content

Commit

Permalink
Merge pull request #117 from pepkit/dev
Browse files Browse the repository at this point in the history
Release 0.7.1
  • Loading branch information
khoroshevskyi authored Jan 23, 2024
2 parents 19299a6 + 5a0ba8f commit 758e973
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 27 deletions.
5 changes: 5 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

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.7.1] -- 2023-01-22
- Fixed bug in Stars annotation
- SQL efficiency improvements
- Added sort by date in stared projects

## [0.7.0] -- 2023-01-17
- Added `pop` to project table and annotation model [#107](https://github.com/pepkit/pepdbagent/issues/107)
- Added `forked_from` feature [#73](https://github.com/pepkit/pepdbagent/issues/73)
Expand Down
2 changes: 1 addition & 1 deletion pepdbagent/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.7.0"
__version__ = "0.7.1"
9 changes: 7 additions & 2 deletions pepdbagent/db_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class Projects(Base):
number_of_stars: Mapped[int] = mapped_column(default=0)
submission_date: Mapped[datetime.datetime]
last_update_date: Mapped[Optional[datetime.datetime]] = mapped_column(
onupdate=deliver_update_date, default=deliver_update_date
default=deliver_update_date, # onupdate=deliver_update_date, # This field should not be updated, while we are adding project to favorites
)
pep_schema: Mapped[Optional[str]]
pop: Mapped[Optional[bool]] = mapped_column(default=False)
Expand Down Expand Up @@ -168,7 +168,9 @@ class User(Base):
id: Mapped[int] = mapped_column(primary_key=True)
namespace: Mapped[str]
stars_mapping: Mapped[List["Stars"]] = relationship(
back_populates="user_mapping", cascade="all, delete-orphan"
back_populates="user_mapping",
cascade="all, delete-orphan",
order_by="Stars.star_date.desc()",
)


Expand All @@ -183,6 +185,9 @@ class Stars(Base):
project_id = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), primary_key=True)
user_mapping: Mapped[List["User"]] = relationship(back_populates="stars_mapping")
project_mapping: Mapped["Projects"] = relationship(back_populates="stars_mapping")
star_date: Mapped[datetime.datetime] = mapped_column(
onupdate=deliver_update_date, default=deliver_update_date
)


class Views(Base):
Expand Down
6 changes: 6 additions & 0 deletions pepdbagent/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,9 @@ class CreateViewDictModel(BaseModel):
project_name: str
project_tag: str
sample_list: List[str]


class RegistryPath(BaseModel):
namespace: str
name: str
tag: Optional[str] = "default"
78 changes: 71 additions & 7 deletions pepdbagent/modules/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
)
from pepdbagent.db_utils import BaseEngine, Projects
from pepdbagent.exceptions import FilterError, ProjectNotFoundError, RegistryPathError
from pepdbagent.models import AnnotationList, AnnotationModel
from pepdbagent.models import AnnotationList, AnnotationModel, RegistryPath
from pepdbagent.utils import convert_date_string_to_date, registry_path_converter, tuple_converter

_LOGGER = logging.getLogger(PKG_NAME)
Expand Down Expand Up @@ -309,7 +309,7 @@ def _get_projects(

if admin is None:
admin = []
statement = select(Projects.id)
statement = select(Projects)

statement = self._add_condition(
statement,
Expand All @@ -325,13 +325,10 @@ def _get_projects(
if pep_type:
statement = statement.where(Projects.pop.is_(pep_type == "pop"))

id_results = self._pep_db_engine.session_execute(statement).all()

results_list = []
with Session(self._sa_engine) as session:
for prj_ids in id_results:
result = session.scalar(select(Projects).where(Projects.id == prj_ids[0]))

results = session.scalars(statement)
for result in results:
results_list.append(
AnnotationModel(
namespace=result.namespace,
Expand Down Expand Up @@ -572,3 +569,70 @@ def get_by_rp_list(

else:
return self.get_by_rp(registry_paths, admin)

def get_projects_list(
self,
namespace: str = None,
search_str: str = None,
admin: Union[str, List[str]] = None,
limit: int = DEFAULT_LIMIT,
offset: int = DEFAULT_OFFSET,
order_by: str = "update_date",
order_desc: bool = False,
filter_by: Optional[Literal["submission_date", "last_update_date"]] = None,
filter_start_date: Optional[str] = None,
filter_end_date: Optional[str] = None,
pep_type: Optional[Literal["pep", "pop"]] = None,
) -> List[RegistryPath]:
"""
Retrieve a list of projects by providing a search string.
This function serves as a lightweight version of the full 'get' function,
returning only a list of registry paths without annotations.
It is designed for use cases where a large list of projects is needed with minimal processing time.
:param namespace: namespace where to search for a project
:param search_str: search string that has to be found in the name or tag
:param admin: True, if user is admin of the namespace [Default: False]
:param limit: limit of return results
:param offset: number of results off set (that were already showed)
:param order_by: sort the result-set by the information
Options: ["name", "update_date", "submission_date"]
[Default: "update_date"]
:param order_desc: Sort the records in descending order. [Default: False]
:param filter_by: data to use filter on.
Options: ["submission_date", "last_update_date"]
[Default: filter won't be used]
:param filter_start_date: Filter start date. Format: "YYYY:MM:DD"
:param filter_end_date: Filter end date. Format: "YYYY:MM:DD". if None: present date will be used
:param pep_type: Get pep with specified type. Options: ["pep", "pop"]. Default: None, get all peps
:return: list of found projects with their annotations.
"""
_LOGGER.info(f"Running project search: (namespace: {namespace}, query: {search_str}.")

if admin is None:
admin = []
statement = select(Projects.namespace, Projects.name, Projects.tag)

statement = self._add_condition(
statement,
namespace=namespace,
search_str=search_str,
admin_list=admin,
)
statement = self._add_date_filter_if_provided(
statement, filter_by, filter_start_date, filter_end_date
)
statement = self._add_order_by_keyword(statement, by=order_by, desc=order_desc)
statement = statement.limit(limit).offset(offset)
if pep_type:
statement = statement.where(Projects.pop.is_(pep_type == "pop"))

results_list = []
with Session(self._sa_engine) as session:
results = session.execute(statement)

for result in results:
results_list.append(
RegistryPath(namespace=result[0], name=result[1], tag=result[2])
)
return results_list
2 changes: 1 addition & 1 deletion pepdbagent/modules/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def get(

try:
with Session(self._sa_engine) as session:
found_prj = session.scalars(statement).one()
found_prj = session.scalar(statement)

if found_prj:
_LOGGER.info(
Expand Down
32 changes: 16 additions & 16 deletions pepdbagent/modules/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
)

from pepdbagent.db_utils import BaseEngine, User, Stars, Projects
from pepdbagent.modules.project import PEPDatabaseProject
from pepdbagent.models import AnnotationList, AnnotationModel
from pepdbagent.exceptions import ProjectNotInFavorites, ProjectAlreadyInFavorites

Expand Down Expand Up @@ -114,7 +113,7 @@ def remove_project_from_favorites(
:return: None
"""
_LOGGER.debug(
f"Removing project {project_namespace}/{project_name}:{project_tag} from fProjectNotInFavorites for user {namespace}"
f"Removing project {project_namespace}/{project_name}:{project_tag} from favorites in {namespace}"
)

user_id = self.get_user_id(namespace)
Expand Down Expand Up @@ -166,22 +165,23 @@ def get_favorites(self, namespace: str) -> AnnotationList:
number_of_projects = len([kk.project_mapping for kk in query_result.stars_mapping])
project_list = []
for prj_list in query_result.stars_mapping:
prj = prj_list.project_mapping
project_list.append(
AnnotationModel(
namespace=prj_list.project_mapping.namespace,
name=prj_list.project_mapping.name,
tag=prj_list.project_mapping.tag,
is_private=prj_list.project_mapping.private,
number_of_samples=prj_list.project_mapping.number_of_samples,
description=prj_list.project_mapping.description,
last_update_date=str(prj_list.project_mapping.last_update_date),
submission_date=str(prj_list.project_mapping.submission_date),
digest=prj_list.project_mapping.digest,
pep_schema=prj_list.project_mapping.pep_schema,
pop=prj_list.project_mapping.pop,
stars_number=prj_list.project_mapping.number_of_stars,
forked_from=f"{prj_list.project_mapping.namespace}/{prj_list.project_mapping.name}:{prj_list.project_mapping.tag}"
if prj_list.project_mapping
namespace=prj.namespace,
name=prj.name,
tag=prj.tag,
is_private=prj.private,
number_of_samples=prj.number_of_samples,
description=prj.description,
last_update_date=str(prj.last_update_date),
submission_date=str(prj.submission_date),
digest=prj.digest,
pep_schema=prj.pep_schema,
pop=prj.pop,
stars_number=prj.number_of_stars,
forked_from=f"{prj.forked_from_mapping.namespace}/{prj.forked_from_mapping.name}:{prj.forked_from_mapping.tag}"
if prj.forked_from_mapping
else None,
)
)
Expand Down
14 changes: 14 additions & 0 deletions pepdbagent/modules/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def get(
_subsample_dict: dict
}
"""
_LOGGER.debug(f"Get view {view_name} from {namespace}/{name}:{tag}")
view_statement = select(Views).where(
and_(
Views.project_mapping.has(namespace=namespace, name=name, tag=tag),
Expand Down Expand Up @@ -106,6 +107,7 @@ def get_annotation(
description: str,
number_of_samples: int}
"""
_LOGGER.debug(f"Get annotation for view {view_name} in {namespace}/{name}:{tag}")
view_statement = select(Views).where(
and_(
Views.project_mapping.has(namespace=namespace, name=name, tag=tag),
Expand Down Expand Up @@ -149,6 +151,7 @@ def create(
:param description: description of the view
retrun: None
"""
_LOGGER.debug(f"Creating view {view_name} with provided info: (view_dict: {view_dict})")
if isinstance(view_dict, dict):
view_dict = CreateViewDictModel(**view_dict)

Expand Down Expand Up @@ -205,6 +208,9 @@ def delete(
:param view_name: name of the view
:return: None
"""
_LOGGER.debug(
f"Deleting view {view_name} from {project_namespace}/{project_name}:{project_tag}"
)
view_statement = select(Views).where(
and_(
Views.project_mapping.has(
Expand Down Expand Up @@ -241,6 +247,9 @@ def add_sample(
:param sample_name: sample name
:return: None
"""
_LOGGER.debug(
f"Adding sample {sample_name} to view {view_name} in {namespace}/{name}:{tag}"
)
if isinstance(sample_name, str):
sample_name = [sample_name]
view_statement = select(Views).where(
Expand Down Expand Up @@ -296,6 +305,9 @@ def remove_sample(
:param sample_name: sample name
:return: None
"""
_LOGGER.debug(
f"Removing sample {sample_name} from view {view_name} in {namespace}/{name}:{tag}"
)
view_statement = select(Views).where(
and_(
Views.project_mapping.has(namespace=namespace, name=name, tag=tag),
Expand Down Expand Up @@ -341,6 +353,7 @@ def get_snap_view(
:param raw: retrieve unprocessed (raw) PEP dict.
:return: peppy.Project object
"""
_LOGGER.debug(f"Creating snap view for {namespace}/{name}:{tag}")
project_statement = select(Projects).where(
and_(
Projects.namespace == namespace,
Expand Down Expand Up @@ -386,6 +399,7 @@ def get_views_annotation(
:param tag: tag of the project
:return: list of views of the project
"""
_LOGGER.debug(f"Get views annotation for {namespace}/{name}:{tag}")
statement = select(Views).where(
Views.project_mapping.has(namespace=namespace, name=name, tag=tag),
)
Expand Down
15 changes: 15 additions & 0 deletions tests/test_pepagent.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,21 @@ def test_search_incorrect_incorrect_pep_type(
with pytest.raises(ValueError):
initiate_pepdb_con.annotation.get(namespace=namespace, pep_type="incorrect")

@pytest.mark.parametrize(
"namespace, query, found_number",
[
["namespace1", "ame", 2],
],
)
def test_project_list_without_annotation(
self, initiate_pepdb_con, namespace, query, found_number
):
result = initiate_pepdb_con.annotation.get_projects_list(
namespace=namespace,
search_str=query,
)
assert len(result) == found_number


@pytest.mark.skipif(
not db_setup(),
Expand Down

0 comments on commit 758e973

Please sign in to comment.