Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions app/classrooms/models/classrooms_db.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from datetime import datetime
from enum import StrEnum, auto
from typing import Annotated, ClassVar, Literal
from typing import Annotated, Any, ClassVar, Literal

from pydantic import AwareDatetime, Field
from pydantic import AwareDatetime, BaseModel, Field
from pydantic_marshals.sqlalchemy import MappedModel
from sqlalchemy import DateTime, Enum, String, Text, select, update
from sqlalchemy import DateTime, Enum, Select, String, Text, select, update
from sqlalchemy.orm import Mapped, mapped_column

from app.common.config import Base
Expand All @@ -31,6 +31,22 @@ class ClassroomStatus(StrEnum):
]


class ClassroomCursorSchema(BaseModel):
created_at: AwareDatetime


class ClassroomFiltersSchema(BaseModel):
statuses: Annotated[set[ClassroomStatus], Field(min_length=1)] | None = None
kinds: Annotated[set[ClassroomKind], Field(min_length=1)] | None = None
subject_ids: Annotated[set[int], Field(min_length=1, max_length=10)] | None = None


class ClassroomSearchRequestSchema(BaseModel):
cursor: ClassroomCursorSchema | None = None
limit: Annotated[int, Field(gt=0, le=100)] = 50
filters: ClassroomFiltersSchema = ClassroomFiltersSchema()


class Classroom(Base):
__tablename__: str | None = "classrooms"

Expand Down Expand Up @@ -71,6 +87,26 @@ class Classroom(Base):
],
)

@classmethod
def select_by_search_params(
cls,
stmt: Select[Any],
search_params: ClassroomSearchRequestSchema,
) -> Select[tuple[Any]]:
if search_params.filters.statuses is not None:
stmt = stmt.filter(cls.status.in_(search_params.filters.statuses))

if search_params.filters.kinds is not None:
stmt = stmt.filter(cls.kind.in_(search_params.filters.kinds))

if search_params.filters.subject_ids is not None:
stmt = stmt.filter(cls.subject_id.in_(search_params.filters.subject_ids))

if search_params.cursor is not None:
stmt = stmt.filter(cls.created_at < search_params.cursor.created_at)

return stmt.order_by(cls.created_at.desc()).limit(search_params.limit)


class IndividualClassroom(Classroom):
__tablename__ = None
Expand Down
22 changes: 20 additions & 2 deletions app/classrooms/routes/classrooms_student_rst.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
from app.classrooms.models.classrooms_db import (
AnyClassroom,
Classroom,
ClassroomSearchRequestSchema,
IndividualClassroom,
StudentClassroomResponseSchema,
)
from app.classrooms.models.enrollments_db import Enrollment
from app.classrooms.services import classrooms_svc
from app.common.dependencies.authorization_dep import AuthorizationData
from app.common.fastapi_ext import APIRouterExt
from app.common.sqlalchemy_ext import db
Expand All @@ -23,9 +25,10 @@
@router.get(
path="/roles/student/classrooms/",
response_model=list[StudentClassroomResponseSchema],
summary="List paginated student classrooms for the current user",
summary="Use POST /roles/student/classrooms/searches/ instead",
deprecated=True,
)
async def list_classrooms(
async def list_classrooms_old(
auth_data: AuthorizationData,
created_before: AwareDatetime | None = None,
limit: Annotated[int, Field(gt=0, le=100)] = 50,
Expand All @@ -45,6 +48,21 @@ async def list_classrooms(
return await db.get_all(stmt.order_by(Classroom.created_at.desc()).limit(limit))


@router.post(
path="/roles/student/classrooms/searches/",
response_model=list[StudentClassroomResponseSchema],
summary="List paginated student classrooms for the current user",
)
async def list_classrooms(
auth_data: AuthorizationData,
data: ClassroomSearchRequestSchema,
) -> Sequence[Classroom]:
return await classrooms_svc.retrieve_paginated_classrooms_by_student_id(
student_id=auth_data.user_id,
search_params=data,
)


@router.get(
path="/roles/student/classrooms/{classroom_id}/",
response_model=StudentClassroomResponseSchema,
Expand Down
22 changes: 20 additions & 2 deletions app/classrooms/routes/classrooms_tutor_rst.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
from app.classrooms.models.classrooms_db import (
AnyClassroom,
Classroom,
ClassroomSearchRequestSchema,
GroupClassroom,
IndividualClassroom,
TutorClassroomResponseSchema,
UserClassroomStatus,
)
from app.classrooms.models.tutorships_db import Tutorship
from app.classrooms.services import classrooms_svc
from app.common.bridges.autocomplete_bdg import SubjectNotFoundException
from app.common.config_bdg import autocomplete_bridge
from app.common.dependencies.authorization_dep import AuthorizationData
Expand All @@ -32,9 +34,10 @@
@router.get(
path="/roles/tutor/classrooms/",
response_model=list[TutorClassroomResponseSchema],
summary="List paginated tutor classrooms for the current user",
summary="Use POST /roles/tutor/classrooms/searches/ instead",
deprecated=True,
)
async def list_classrooms(
async def list_classrooms_old(
auth_data: AuthorizationData,
created_before: AwareDatetime | None = None,
limit: Annotated[int, Field(gt=0, le=100)] = 50,
Expand All @@ -45,6 +48,21 @@ async def list_classrooms(
return await db.get_all(stmt.order_by(Classroom.created_at.desc()).limit(limit))


@router.post(
path="/roles/tutor/classrooms/searches/",
response_model=list[TutorClassroomResponseSchema],
summary="List paginated tutor classrooms for the current user",
)
async def list_classrooms(
auth_data: AuthorizationData,
data: ClassroomSearchRequestSchema,
) -> Sequence[Classroom]:
return await classrooms_svc.retrieve_paginated_classrooms_by_tutor_id(
tutor_id=auth_data.user_id,
search_params=data,
)


class SubjectResponses(Responses):
SUBJECT_NOT_FOUND = status.HTTP_404_NOT_FOUND, "Subject not found"

Expand Down
40 changes: 40 additions & 0 deletions app/classrooms/services/classrooms_svc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from collections.abc import Sequence

from sqlalchemy import or_, select

from app.classrooms.models.classrooms_db import (
Classroom,
ClassroomSearchRequestSchema,
IndividualClassroom,
)
from app.classrooms.models.enrollments_db import Enrollment
from app.common.sqlalchemy_ext import db


async def retrieve_paginated_classrooms_by_student_id(
student_id: int,
search_params: ClassroomSearchRequestSchema,
) -> Sequence[Classroom]:
stmt = Classroom.select_by_search_params(
stmt=select(Classroom)
.join(Enrollment, isouter=True)
.filter(
or_(
Enrollment.student_id == student_id,
IndividualClassroom.student_id == student_id,
)
),
search_params=search_params,
)
return await db.get_all(stmt=stmt)


async def retrieve_paginated_classrooms_by_tutor_id(
tutor_id: int,
search_params: ClassroomSearchRequestSchema,
) -> Sequence[Classroom]:
stmt = Classroom.select_by_search_params(
stmt=select(Classroom).filter_by(tutor_id=tutor_id),
search_params=search_params,
)
return await db.get_all(stmt=stmt)
Loading