From 0caa0f21b1a7cb981774507d444c80cf096a2f38 Mon Sep 17 00:00:00 2001 From: Artem Bratyashin Date: Sat, 2 May 2026 09:19:39 +0300 Subject: [PATCH 1/7] first version is ready --- modal_backend/models/db.py | 26 +++++++++++++++++++++++++- modal_backend/routes/notes.py | 26 +++++++++++++++++++++++--- modal_backend/utils/services.py | 22 +++++++++++++++++++--- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/modal_backend/models/db.py b/modal_backend/models/db.py index d5b36a6..cb86916 100644 --- a/modal_backend/models/db.py +++ b/modal_backend/models/db.py @@ -10,8 +10,12 @@ ForeignKey, Integer, String, + cast, + desc, + or_, true, ) +from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.ext.hybrid import hybrid_method from sqlalchemy.orm import Mapped, mapped_column @@ -79,10 +83,30 @@ class Note(BaseDbModel): @hybrid_method def search_by_type_id(self, query: int) -> bool: - if not self.query: + if query is None: return true() return Note.type_id == query + @hybrid_method + def search_by_group_ids(self, query: list[int]) -> bool: + if not query: + return true() + group_ids_jsonb = cast(Note.group_ids, JSONB) + return or_(*(group_ids_jsonb.contains([qid]) for qid in query)) + + @hybrid_method + def search_by_service_ids(self, query: list[int]) -> bool: + if not query: + return true() + service_ids_jsonb = cast(Note.service_ids, JSONB) + return or_(*(service_ids_jsonb.contains([qid]) for qid in query)) + + @hybrid_method + def order_by_start_ts( + self, query: str, asc_order: bool + ) -> UnaryExpression[datetime.datetime] | InstrumentedAttribute: + return getattr(Note, query) if asc_order else desc(getattr(Note, query)) + class NoteResponse(BaseDbModel): __tablename__ = "note_response" diff --git a/modal_backend/routes/notes.py b/modal_backend/routes/notes.py index 1fc2b82..3a6eea6 100644 --- a/modal_backend/routes/notes.py +++ b/modal_backend/routes/notes.py @@ -40,15 +40,35 @@ async def get_notes( default=None, ), asc_order: bool = False, - user=Depends(UnionAuth()), + #user=Depends(UnionAuth()), ) -> list[NoteGet]: """ - Получить список модалок по type_id. + Получить список модалок по фильтрам + + `limit` - максимальное количество возвращаемых комментариев + + `offset` - смещение, определяющее, с какого по порядку комментария начинать выборку. + Если без смещения возвращается комментарий с условным номером N, + то при значении offset = X будет возвращаться комментарий с номером N + X + + `status` - возможные значения `"active", "archived"`. + Если передано `'active'` - возвращается список активных комментариев + Если передано `'archived'` - возвращается список архивированных комментариев + + `type_id` - вернет все модалки конкретного типа по type_id типа + + `groups_id` - вернет все модалки с конкретным groups_id + + `services_id` - вернет все модалки сервиса по id + + `asc_order` -Если передано true, сортировать в порядке возрастания. Иначе - в порядке убывания В случае несуществующего type_id ошибка ObjectNotFound """ - notes = await NoteService.get_note_by_type_id(db, type_id, limit, offset, groups_id, services_id, status, asc_order) + notes = await NoteService.get_notes_by_filters( + db, type_id, limit, offset, groups_id, services_id, status, asc_order + ) return [NoteGet.model_validate(note) for note in notes] diff --git a/modal_backend/utils/services.py b/modal_backend/utils/services.py index 9c21069..40ccf96 100644 --- a/modal_backend/utils/services.py +++ b/modal_backend/utils/services.py @@ -11,7 +11,7 @@ class NoteService: """ @classmethod - async def get_note_by_type_id( + async def get_notes_by_filters( cls, db: Session, type_id: int, @@ -22,8 +22,24 @@ async def get_note_by_type_id( status: str, asc_order: bool, ): - # add filter logic - notes = Note.query(session=db.session).filter(Note.search_by_type_id(type_id)).limit(limit).offset(offset).all() + notes_query = ( + Note.query(session=db.session) + .filter(Note.search_by_type_id(type_id)) + .filter(Note.search_by_group_ids(groups_id)) + .filter(Note.search_by_service_ids(services_id)) + ) + + if status == 'active': + notes_query = notes_query.filter(Note.is_deleted == 0) + if status == 'archived': + notes_query = notes_query.filter(Note.is_deleted == 1) + + notes_query = notes_query.order_by(Note.order_by_start_ts("start_ts", asc_order)) + notes = notes_query.limit(limit).offset(offset).all() + + if not notes: + raise ObjectNotFound(Note, 'all') + return notes @staticmethod From 38e7f542ac8e7c4c62df815c56a95a66d369514a Mon Sep 17 00:00:00 2001 From: Artem Bratyashin Date: Sat, 2 May 2026 09:53:29 +0300 Subject: [PATCH 2/7] need to be tested --- modal_backend/routes/notes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modal_backend/routes/notes.py b/modal_backend/routes/notes.py index 3a6eea6..547a9b3 100644 --- a/modal_backend/routes/notes.py +++ b/modal_backend/routes/notes.py @@ -40,7 +40,7 @@ async def get_notes( default=None, ), asc_order: bool = False, - #user=Depends(UnionAuth()), + user=Depends(UnionAuth()), ) -> list[NoteGet]: """ Получить список модалок по фильтрам From ad4fba2116fcf54b1e46ff342e15fbb6042e7f57 Mon Sep 17 00:00:00 2001 From: Artem Bratyashin Date: Mon, 25 May 2026 21:11:00 +0300 Subject: [PATCH 3/7] It's ready --- modal_backend/utils/services.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modal_backend/utils/services.py b/modal_backend/utils/services.py index 40ccf96..57362cc 100644 --- a/modal_backend/utils/services.py +++ b/modal_backend/utils/services.py @@ -30,9 +30,9 @@ async def get_notes_by_filters( ) if status == 'active': - notes_query = notes_query.filter(Note.is_deleted == 0) + notes_query = notes_query.filter(Note.status == "active") if status == 'archived': - notes_query = notes_query.filter(Note.is_deleted == 1) + notes_query = notes_query.filter(Note.status == "archived") notes_query = notes_query.order_by(Note.order_by_start_ts("start_ts", asc_order)) notes = notes_query.limit(limit).offset(offset).all() From 0efb273b5339eb8c5bc8d245dee0eb515280647c Mon Sep 17 00:00:00 2001 From: Artem Bratyashin Date: Wed, 24 Jun 2026 20:53:13 +0300 Subject: [PATCH 4/7] Made some changes but need test --- modal_backend/models/db.py | 11 ++++++----- modal_backend/utils/services.py | 23 +++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/modal_backend/models/db.py b/modal_backend/models/db.py index cb86916..d575ee6 100644 --- a/modal_backend/models/db.py +++ b/modal_backend/models/db.py @@ -11,7 +11,6 @@ Integer, String, cast, - desc, or_, true, ) @@ -102,10 +101,12 @@ def search_by_service_ids(self, query: list[int]) -> bool: return or_(*(service_ids_jsonb.contains([qid]) for qid in query)) @hybrid_method - def order_by_start_ts( - self, query: str, asc_order: bool - ) -> UnaryExpression[datetime.datetime] | InstrumentedAttribute: - return getattr(Note, query) if asc_order else desc(getattr(Note, query)) + def search_by_status(self, status: Optional[str] = None) -> bool: + if status == "active": + return Note.status == "active" + if status == "archived": + return Note.status == "archived" + return true() class NoteResponse(BaseDbModel): diff --git a/modal_backend/utils/services.py b/modal_backend/utils/services.py index 57362cc..dbce68f 100644 --- a/modal_backend/utils/services.py +++ b/modal_backend/utils/services.py @@ -1,3 +1,4 @@ +from alembic.environment import List, Optional from requests import Session from modal_backend.exceptions import AlreadyExists, ObjectNotFound @@ -14,27 +15,25 @@ class NoteService: async def get_notes_by_filters( cls, db: Session, - type_id: int, - limit: int, - offset: int, - groups_id: list[int], - services_id: list[int], - status: str, - asc_order: bool, + type_id: Optional[int] = None, + limit: int = 100, + offset: int = 0, + groups_id: Optional[List[int]] = None, + services_id: Optional[List[int]] = None, + status: Optional[str] = None, + asc_order: Optional[bool] = True, ): notes_query = ( Note.query(session=db.session) .filter(Note.search_by_type_id(type_id)) .filter(Note.search_by_group_ids(groups_id)) .filter(Note.search_by_service_ids(services_id)) + .filter(Note.search_by_status(status)) ) - if status == 'active': - notes_query = notes_query.filter(Note.status == "active") - if status == 'archived': - notes_query = notes_query.filter(Note.status == "archived") + order_expr = Note.start_ts.asc() if asc_order else Note.start_ts.desc() + notes_query = notes_query.order_by(order_expr) - notes_query = notes_query.order_by(Note.order_by_start_ts("start_ts", asc_order)) notes = notes_query.limit(limit).offset(offset).all() if not notes: From 961cfd0ed5b03acf5c1460c9720f04099b8f7853 Mon Sep 17 00:00:00 2001 From: Artem Bratyashin Date: Fri, 26 Jun 2026 18:58:37 +0300 Subject: [PATCH 5/7] Little fixes --- modal_backend/models/db.py | 3 +-- modal_backend/routes/notes.py | 6 +++--- modal_backend/utils/services.py | 14 +++++++------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/modal_backend/models/db.py b/modal_backend/models/db.py index d575ee6..b339cb3 100644 --- a/modal_backend/models/db.py +++ b/modal_backend/models/db.py @@ -104,9 +104,8 @@ def search_by_service_ids(self, query: list[int]) -> bool: def search_by_status(self, status: Optional[str] = None) -> bool: if status == "active": return Note.status == "active" - if status == "archived": + elif status == "archived": return Note.status == "archived" - return true() class NoteResponse(BaseDbModel): diff --git a/modal_backend/routes/notes.py b/modal_backend/routes/notes.py index 547a9b3..01f023e 100644 --- a/modal_backend/routes/notes.py +++ b/modal_backend/routes/notes.py @@ -30,8 +30,6 @@ @note.get("", response_model=list[NoteGet]) async def get_notes( - limit: int = 10, - offset: int = 0, type_id: int = Query(None), groups_id: list[int] = Query(None), services_id: list[int] = Query(None), @@ -40,6 +38,8 @@ async def get_notes( default=None, ), asc_order: bool = False, + limit: int = 10, + offset: int = 0, user=Depends(UnionAuth()), ) -> list[NoteGet]: """ @@ -67,7 +67,7 @@ async def get_notes( """ notes = await NoteService.get_notes_by_filters( - db, type_id, limit, offset, groups_id, services_id, status, asc_order + db, type_id, groups_id, services_id, status, asc_order, limit, offset ) return [NoteGet.model_validate(note) for note in notes] diff --git a/modal_backend/utils/services.py b/modal_backend/utils/services.py index dbce68f..08c35a2 100644 --- a/modal_backend/utils/services.py +++ b/modal_backend/utils/services.py @@ -15,13 +15,13 @@ class NoteService: async def get_notes_by_filters( cls, db: Session, - type_id: Optional[int] = None, - limit: int = 100, - offset: int = 0, - groups_id: Optional[List[int]] = None, - services_id: Optional[List[int]] = None, - status: Optional[str] = None, - asc_order: Optional[bool] = True, + type_id: int, + groups_id: int, + services_id: int, + status: str, + asc_order: bool, + limit: Optional[int] = None, + offset: Optional[int] = None, ): notes_query = ( Note.query(session=db.session) From 934b946a9ff3b42988db75f6ea3a28e8ed673ce6 Mon Sep 17 00:00:00 2001 From: Artem Bratyashin Date: Sat, 27 Jun 2026 20:34:17 +0300 Subject: [PATCH 6/7] Turned back --- modal_backend/models/db.py | 1 + modal_backend/routes/notes.py | 2 +- modal_backend/utils/services.py | 12 ++++++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/modal_backend/models/db.py b/modal_backend/models/db.py index b339cb3..0c8f5d8 100644 --- a/modal_backend/models/db.py +++ b/modal_backend/models/db.py @@ -106,6 +106,7 @@ def search_by_status(self, status: Optional[str] = None) -> bool: return Note.status == "active" elif status == "archived": return Note.status == "archived" + return True class NoteResponse(BaseDbModel): diff --git a/modal_backend/routes/notes.py b/modal_backend/routes/notes.py index 01f023e..91ea25a 100644 --- a/modal_backend/routes/notes.py +++ b/modal_backend/routes/notes.py @@ -67,7 +67,7 @@ async def get_notes( """ notes = await NoteService.get_notes_by_filters( - db, type_id, groups_id, services_id, status, asc_order, limit, offset + db, asc_order, limit, offset, type_id, groups_id, services_id, status ) return [NoteGet.model_validate(note) for note in notes] diff --git a/modal_backend/utils/services.py b/modal_backend/utils/services.py index 08c35a2..a2492bf 100644 --- a/modal_backend/utils/services.py +++ b/modal_backend/utils/services.py @@ -15,13 +15,13 @@ class NoteService: async def get_notes_by_filters( cls, db: Session, - type_id: int, - groups_id: int, - services_id: int, - status: str, + limit: int, + offset: int, asc_order: bool, - limit: Optional[int] = None, - offset: Optional[int] = None, + type_id: int = None, + groups_id: List[int] = None, + services_id: List[int] = None, + status: str = None, ): notes_query = ( Note.query(session=db.session) From 13ff50c0a05ef911a8b8910757882c90e030e681 Mon Sep 17 00:00:00 2001 From: Artem Bratyashin Date: Sun, 28 Jun 2026 18:32:03 +0300 Subject: [PATCH 7/7] It works --- modal_backend/routes/notes.py | 14 +++++++------- modal_backend/utils/services.py | 5 ++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/modal_backend/routes/notes.py b/modal_backend/routes/notes.py index 91ea25a..dcb559b 100644 --- a/modal_backend/routes/notes.py +++ b/modal_backend/routes/notes.py @@ -45,15 +45,15 @@ async def get_notes( """ Получить список модалок по фильтрам - `limit` - максимальное количество возвращаемых комментариев + `limit` - максимальное количество возвращаемых модалок - `offset` - смещение, определяющее, с какого по порядку комментария начинать выборку. - Если без смещения возвращается комментарий с условным номером N, - то при значении offset = X будет возвращаться комментарий с номером N + X + `offset` - смещение, определяющее, с какой по порядку модалки начинать выборку. + Если без смещения возвращается модалка с условным номером N, + то при значении offset = X будет возвращаться модалка с номером N + X `status` - возможные значения `"active", "archived"`. - Если передано `'active'` - возвращается список активных комментариев - Если передано `'archived'` - возвращается список архивированных комментариев + Если передано `'active'` - возвращается список активных модалок + Если передано `'archived'` - возвращается список архивированных модалок `type_id` - вернет все модалки конкретного типа по type_id типа @@ -67,7 +67,7 @@ async def get_notes( """ notes = await NoteService.get_notes_by_filters( - db, asc_order, limit, offset, type_id, groups_id, services_id, status + db, limit, offset, asc_order, type_id, groups_id, services_id, status ) return [NoteGet.model_validate(note) for note in notes] diff --git a/modal_backend/utils/services.py b/modal_backend/utils/services.py index a2492bf..2f84558 100644 --- a/modal_backend/utils/services.py +++ b/modal_backend/utils/services.py @@ -1,4 +1,3 @@ -from alembic.environment import List, Optional from requests import Session from modal_backend.exceptions import AlreadyExists, ObjectNotFound @@ -19,8 +18,8 @@ async def get_notes_by_filters( offset: int, asc_order: bool, type_id: int = None, - groups_id: List[int] = None, - services_id: List[int] = None, + groups_id: list[int] = None, + services_id: list[int] = None, status: str = None, ): notes_query = (