mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add course search functionality with advanced filtering backend
This commit is contained in:
parent
35c2fbc0f7
commit
01132c3745
2 changed files with 94 additions and 1 deletions
|
|
@ -24,6 +24,7 @@ from src.services.courses.courses import (
|
||||||
update_course,
|
update_course,
|
||||||
delete_course,
|
delete_course,
|
||||||
update_course_thumbnail,
|
update_course_thumbnail,
|
||||||
|
search_courses,
|
||||||
)
|
)
|
||||||
from src.services.courses.updates import (
|
from src.services.courses.updates import (
|
||||||
create_update,
|
create_update,
|
||||||
|
|
@ -146,6 +147,24 @@ async def api_get_course_by_orgslug(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/org_slug/{org_slug}/search")
|
||||||
|
async def api_search_courses(
|
||||||
|
request: Request,
|
||||||
|
org_slug: str,
|
||||||
|
query: str,
|
||||||
|
page: int = 1,
|
||||||
|
limit: int = 10,
|
||||||
|
db_session: Session = Depends(get_db_session),
|
||||||
|
current_user: PublicUser = Depends(get_current_user),
|
||||||
|
) -> List[CourseRead]:
|
||||||
|
"""
|
||||||
|
Search courses by title and description
|
||||||
|
"""
|
||||||
|
return await search_courses(
|
||||||
|
request, current_user, org_slug, query, db_session, page, limit
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{course_uuid}")
|
@router.put("/{course_uuid}")
|
||||||
async def api_update_course(
|
async def api_update_course(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import Literal, List
|
from typing import Literal, List
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from sqlmodel import Session, select, or_, and_
|
from sqlmodel import Session, select, or_, and_, text
|
||||||
from src.db.usergroup_resources import UserGroupResource
|
from src.db.usergroup_resources import UserGroupResource
|
||||||
from src.db.usergroup_user import UserGroupUser
|
from src.db.usergroup_user import UserGroupUser
|
||||||
from src.db.organizations import Organization
|
from src.db.organizations import Organization
|
||||||
|
|
@ -214,6 +214,80 @@ async def get_courses_orgslug(
|
||||||
return course_reads
|
return course_reads
|
||||||
|
|
||||||
|
|
||||||
|
async def search_courses(
|
||||||
|
request: Request,
|
||||||
|
current_user: PublicUser | AnonymousUser,
|
||||||
|
org_slug: str,
|
||||||
|
search_query: str,
|
||||||
|
db_session: Session,
|
||||||
|
page: int = 1,
|
||||||
|
limit: int = 10,
|
||||||
|
) -> List[CourseRead]:
|
||||||
|
offset = (page - 1) * limit
|
||||||
|
|
||||||
|
# Base query
|
||||||
|
query = (
|
||||||
|
select(Course)
|
||||||
|
.join(Organization)
|
||||||
|
.where(Organization.slug == org_slug)
|
||||||
|
.where(
|
||||||
|
or_(
|
||||||
|
text(f"LOWER(course.name) LIKE LOWER('%{search_query}%')"),
|
||||||
|
text(f"LOWER(course.description) LIKE LOWER('%{search_query}%')"),
|
||||||
|
text(f"LOWER(course.about) LIKE LOWER('%{search_query}%')"),
|
||||||
|
text(f"LOWER(course.learnings) LIKE LOWER('%{search_query}%')"),
|
||||||
|
text(f"LOWER(course.tags) LIKE LOWER('%{search_query}%')")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(current_user, AnonymousUser):
|
||||||
|
# For anonymous users, only show public courses
|
||||||
|
query = query.where(Course.public == True)
|
||||||
|
else:
|
||||||
|
# For authenticated users, show:
|
||||||
|
# 1. Public courses
|
||||||
|
# 2. Courses not in any UserGroup
|
||||||
|
# 3. Courses in UserGroups where the user is a member
|
||||||
|
# 4. Courses where the user is a resource author
|
||||||
|
query = (
|
||||||
|
query
|
||||||
|
.outerjoin(UserGroupResource, UserGroupResource.resource_uuid == Course.course_uuid) # type: ignore
|
||||||
|
.outerjoin(UserGroupUser, and_(
|
||||||
|
UserGroupUser.usergroup_id == UserGroupResource.usergroup_id,
|
||||||
|
UserGroupUser.user_id == current_user.id
|
||||||
|
))
|
||||||
|
.outerjoin(ResourceAuthor, ResourceAuthor.resource_uuid == Course.course_uuid) # type: ignore
|
||||||
|
.where(or_(
|
||||||
|
Course.public == True,
|
||||||
|
UserGroupResource.resource_uuid == None, # Courses not in any UserGroup # noqa: E711
|
||||||
|
UserGroupUser.user_id == current_user.id, # Courses in UserGroups where user is a member
|
||||||
|
ResourceAuthor.user_id == current_user.id # Courses where user is a resource author
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Apply pagination
|
||||||
|
query = query.offset(offset).limit(limit).distinct()
|
||||||
|
|
||||||
|
courses = db_session.exec(query).all()
|
||||||
|
|
||||||
|
# Fetch authors for each course
|
||||||
|
course_reads = []
|
||||||
|
for course in courses:
|
||||||
|
authors_query = (
|
||||||
|
select(User)
|
||||||
|
.join(ResourceAuthor, ResourceAuthor.user_id == User.id) # type: ignore
|
||||||
|
.where(ResourceAuthor.resource_uuid == course.course_uuid)
|
||||||
|
)
|
||||||
|
authors = db_session.exec(authors_query).all()
|
||||||
|
|
||||||
|
course_read = CourseRead.model_validate(course)
|
||||||
|
course_read.authors = [UserRead.model_validate(author) for author in authors]
|
||||||
|
course_reads.append(course_read)
|
||||||
|
|
||||||
|
return course_reads
|
||||||
|
|
||||||
|
|
||||||
async def create_course(
|
async def create_course(
|
||||||
request: Request,
|
request: Request,
|
||||||
org_id: int,
|
org_id: int,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue