Skip to content
Merged
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
79 changes: 76 additions & 3 deletions app/api/routes/questions.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import json
from typing import List

from fastapi import APIRouter
from fastapi import APIRouter, Depends, Request

from app.schemas.utils import TechDescription, UserPrompt
from app.schemas.utils import QuestionFilters, TechDescription, UserPrompt
from app.services.questions import (add_daily_question_to_mongodb,
add_tech_description_to_store)
from app.services.questions import \
generate_daily_question as generate_daily_question_service
from app.services.questions import get_most_recent_daily_question
from app.services.questions import (get_most_recent_daily_question,
get_question_by_id, list_questions)

router = APIRouter()

Expand Down Expand Up @@ -48,3 +50,74 @@ def get_daily_question():
if recent_question:
return recent_question
return {"message": "No daily question found."}


@router.get("/list_questions")
async def list_questions_endpoint(
request: Request,
page: int = 1,
per_page: int = 10,
title: str | None = None,
tags: str | None = None,
difficulty: str | None = None,
status: str | None = None,
description: str | None = None,
sort_by: str = "created_at",
sort_order: str = "desc"
):
"""
Retrieve all questions with filtering and pagination support.

Parameters:
page: Current page number (starts from 1)
per_page: Number of items per page (10-100)
title: Filter by title (case-insensitive partial match)
tags: Comma-separated list of tags to filter by
difficulty: Filter by difficulty level
status: Filter by status
description: Filter by description (case-insensitive partial match)
sort_by: Field to sort by (default: created_at)
sort_order: Sort order (asc/desc)
"""
# Convert comma-separated tags string to list if provided
tag_list = tags.split(",") if tags else None

result = list_questions(
page=page,
per_page=per_page,
title=title,
tags=tag_list,
difficulty=difficulty,
status=status,
description=description,
sort_by=sort_by,
sort_order=sort_order
)

# Generate next page URL if available
total_pages = result["total_pages"]
current_page = page

if current_page < total_pages:
Comment thread
kevinrawal marked this conversation as resolved.
base_url = str(request.base_url)
query_params = dict(request.query_params)
query_params["page"] = str(current_page + 1)
result["next_page"] = f"{base_url}list_questions?{'&'.join(f'{k}={v}' for k, v in query_params.items())}"
else:
result["next_page"] = None

return result


@router.get("/get_question/{question_id}")
def get_question(question_id: str):
"""
Retrieve a specific question by ID.

Parameters:
question_id: The unique identifier of the question
"""
question = get_question_by_id(question_id)
if question:
return question
return {"message": "Question not found"}
1 change: 1 addition & 0 deletions app/prompts/daily_question_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@
"difficulty": "easy|medium|hard", // Based on required knowledge depth and complexity
"tags": ["string"], // 3-6 relevant technical tags for categorization
"learning_objectives": ["string"] // 2-3 key skills/concepts the question teaches
"categories": ["string"] // 1-2 broader categories the question falls under (e.g., "Web Development", "Data Structures")
}}
"""
2 changes: 1 addition & 1 deletion app/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def start(self, interval_hour: int):
self.job = self.scheduler.add_job(
self._daily_question_task,
"interval",
hour=24,
hours=interval_hour,
id="daily_question_job",
replace_existing=True,
)
Expand Down
26 changes: 25 additions & 1 deletion app/schemas/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from pydantic import BaseModel
from enum import Enum
from typing import List, Optional

from pydantic import BaseModel, Field


class UserPrompt(BaseModel):
Expand All @@ -21,3 +24,24 @@ class TechDescription(BaseModel):

description: str
metadata: TechDescriptionMetadata


class SortOrder(str, Enum):
"""Sort order enum."""
ASC = "asc"
DESC = "desc"


class QuestionFilters(BaseModel):
"""Schema for question list filters."""
title: Optional[str] = None
tags: Optional[List[str]] = None
difficulty: Optional[str] = None
status: Optional[str] = None
description: Optional[str] = None
skills: Optional[List[str]] = None
categories: Optional[List[str]] = None
page: int = Field(default=1, gt=0)
per_page: int = Field(default=10, gt=0, le=100)
sort_by: str = Field(default="created_at")
sort_order: SortOrder = SortOrder.DESC
105 changes: 105 additions & 0 deletions app/services/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def add_daily_question_to_mongodb(question: dict) -> None:
"tags": question.get("tags", []),
"difficulty": question.get("difficulty", "None"),
"status": "published",
"skills": question.get("learning_objectives", []),
"categories": question.get("categories", []),
}
questions_collection.insert_one(question_document)

Expand Down Expand Up @@ -62,5 +64,108 @@ def get_most_recent_daily_question():
"difficulty": recent_question["difficulty"],
"status": recent_question["status"],
"hints": recent_question.get("hints", []),
"skills": recent_question.get("learning_objectives", []),
"categories": recent_question.get("categories", []),
}
return None


def list_questions(
page: int = 1,
per_page: int = 10,
title: str = None,
tags: list = None,
difficulty: str = None,
status: str = None,
description: str = None,
skills: list = None,
categories: list = None,
sort_by: str = "created_at",
sort_order: str = "desc"
) -> dict:
"""
List questions with filtering and pagination support.
"""
# Build the filter query
query = {}
if title:
query["title"] = {"$regex": title, "$options": "i"}
if tags:
query["tags"] = {"$in": tags}
if difficulty:
query["difficulty"] = difficulty
if status:
query["status"] = status
if skills:
query["learning_objectives"] = {"$in": skills}
if categories:
query["categories"] = {"$in": categories}
if description:
query["description"] = {"$regex": description, "$options": "i"}

# Calculate skip and limit for pagination
skip = (page - 1) * per_page

# Set sort order
sort_direction = -1 if sort_order.lower() == "desc" else 1
sort_criteria = [(sort_by, sort_direction)]

# Get total count for pagination
total_questions = questions_collection.count_documents(query)

# Fetch questions with pagination
questions = questions_collection.find(
query,
skip=skip,
limit=per_page,
sort=sort_criteria
)

# Convert cursor to list and format response
questions_list = []
for q in questions:
questions_list.append({
"id": q["_id"],
"title": q["title"],
"description": q["description"],
"created_at": q["created_at"],
"likes": q["likes"],
"dislikes": q["dislikes"],
"tags": q["tags"],
"difficulty": q["difficulty"],
"status": q["status"],
"hints": q.get("hints", []),
"skills": q.get("learning_objectives", []),
"categories": q.get("categories", [])
})

return {
"questions": questions_list,
"total": total_questions,
"page": page,
"per_page": per_page,
"total_pages": (total_questions + per_page - 1) // per_page
}


def get_question_by_id(question_id: str) -> dict:
"""
Get a specific question by ID.
"""
question = questions_collection.find_one({"_id": question_id})
if question:
return {
"id": question["_id"],
"title": question["title"],
"description": question["description"],
"created_at": question["created_at"],
"likes": question["likes"],
"dislikes": question["dislikes"],
"tags": question["tags"],
"difficulty": question["difficulty"],
"status": question["status"],
"hints": question.get("hints", []),
"skills": question.get("learning_objectives", []),
"categories": question.get("categories", [])
}
return None