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
4 changes: 4 additions & 0 deletions .gitignore
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Push sample.env and add .env to gitignore

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
backend/.env
backend/venv
backend/mem0_client.py
backend/dashboard.py
6 changes: 5 additions & 1 deletion README.md
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to dockerise the application.
The docker files can go under the global project along side README.md

Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
# AITutor
# AITutor backend

GET /api/survey/questions - get all questions
GET /api/survey/questions/{category} - get questions by category
POST /api/survey/submit - submit answers
Binary file added backend/__pycache__/app.cpython-311.pyc
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add to .gitignore

Binary file not shown.
Binary file added backend/__pycache__/config.cpython-311.pyc
Binary file not shown.
Binary file added backend/__pycache__/main.cpython-311.pyc
Binary file not shown.
Binary file added backend/__pycache__/mem0_client.cpython-311.pyc
Binary file not shown.
Binary file added backend/app/__pycache__/main.cpython-311.pyc
Binary file not shown.
Empty file added backend/app/api/__init__.py
Empty file.
Binary file added backend/app/api/__pycache__/__init__.cpython-311.pyc
Binary file not shown.
Binary file not shown.
73 changes: 73 additions & 0 deletions backend/app/api/survey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from fastapi import APIRouter, HTTPException, Depends
from typing import List
from app.models.survey import SurveyQuestion, UserSurveyResponse
from app.services.survey import SurveyService
from app.services.mem0 import Mem0Service
from config import get_settings

router = APIRouter(prefix="/survey", tags=["survey"])

@router.get("/questions", response_model=List[SurveyQuestion])
async def get_survey_questions(
survey_service: SurveyService = Depends(lambda: SurveyService())
):
"""
Retrieves all available survey questions.
Returns a list of questions with their options.
"""
return survey_service.get_all_questions()

@router.get("/questions/{category}", response_model=List[SurveyQuestion])
async def get_questions_by_category(
category: str,
survey_service: SurveyService = Depends(lambda: SurveyService())
):
"""
Retrieves questions for a specific category.
Args:
category: The category of questions to retrieve (e.g., 'learning_format', 'learning_pace')
Returns:
List of questions for the specified category
"""
questions = survey_service.get_questions_by_category(category)
if not questions:
raise HTTPException(
status_code=404,
detail=f"No questions found for category: {category}"
)
return questions

@router.post("/submit")
async def submit_survey(
response: UserSurveyResponse,
survey_service: SurveyService = Depends(lambda: SurveyService()),
settings = Depends(get_settings)
):
"""
Submits survey responses and stores them in Mem0.ai.
Args:
response: User's survey responses including user_id and answers
Returns:
Processed survey data and initial learning path
"""
# Process survey responses
processed_data = survey_service.process_answers(response.dict())

# Store in Mem0.ai and get learning path
mem0_service = Mem0Service(api_key=settings.MEM0_API_KEY)
try:
result = await mem0_service.store_survey_response(
response.user_id,
processed_data
)
return {
"status": "success",
"message": "Survey responses successfully processed",
"learning_path": result,
"user_id": response.user_id
}
except Exception as e:
raise HTTPException(
status_code=400,
detail=f"Failed to process survey: {str(e)}"
)
Empty file added backend/app/api/tutor.py
Empty file.
49 changes: 49 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from fastapi import FastAPI
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep all endpoints under api folder

from app.api import survey
from fastapi.middleware.cors import CORSMiddleware
from config import get_settings

# Initialize FastAPI app
app = FastAPI(
title="AI Tutor API",
description="API for personalized JavaScript learning experience",
version="1.0.0"
)

# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, replace with actual frontend domain
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Include routers
app.include_router(survey.router, prefix="/api")

@app.get("/")
async def root():
"""
Root endpoint returning API information
"""
return {
"message": "Welcome to AI Tutor API",
"version": "1.0.0",
"documentation": "/docs",
"health_check": "/health"
}

@app.get("/health")
async def health_check():
"""
Health check endpoint for monitoring
"""
return {
"status": "healthy",
"api_version": "1.0.0"
}

if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Empty file added backend/app/models/__init__.py
Empty file.
Binary file not shown.
Binary file not shown.
20 changes: 20 additions & 0 deletions backend/app/models/survey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pydantic import BaseModel
from typing import List, Optional

class SurveyOption(BaseModel):
id: int
text: str

class SurveyQuestion(BaseModel):
id: int
question: str
options: List[SurveyOption]
category: str

class UserAnswer(BaseModel):
question_id: int
selected_option_id: int

class UserSurveyResponse(BaseModel):
user_id: str
answers: List[UserAnswer]
22 changes: 22 additions & 0 deletions backend/app/models/tutor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from pydantic import BaseModel
from typing import Optional, Dict, List

class LearningPreferences(BaseModel):
learning_format: str
weekly_time: str
learning_pace: str
theory_vs_practice: str
independence_level: str

class LearningSession(BaseModel):
user_id: str
topic: str
session_type: str
completion_status: str
feedback: Optional[str]

class LearningPath(BaseModel):
user_id: str
current_topic: str
next_topics: List[str]
recommendations: Dict[str, str]
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
58 changes: 58 additions & 0 deletions backend/app/services/mem0.py
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to be adapted to our usecase

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import aiohttp
from typing import Dict, Any, Optional
import json

class Mem0Service:
def __init__(self, api_key: str, base_url: str = "https://api.mem0.ai/v1"):
self.api_key = api_key
self.base_url = base_url
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}

async def store_survey_response(self, user_id: str, survey_data: Dict) -> Dict[str, Any]:
"""Stores survey responses in Mem0.ai"""
prompt = f"""
Analyze the learning preferences for user {user_id}:
{json.dumps(survey_data, indent=2)}

Based on these preferences, create a personalized learning path and provide recommendations.
Include:
1. Suggested learning format
2. Recommended pace
3. Theory/practice balance
4. Initial topics to cover
"""

async with aiohttp.ClientSession() as session:
async with session.post(
f"{self.base_url}/store",
headers=self.headers,
json={
"user_id": user_id,
"content": survey_data,
"metadata": {"type": "survey_response"}
}
) as response:
return await response.json()

async def get_learning_path(self, user_id: str) -> Dict[str, Any]:
"""Retrieves personalized learning path based on survey responses"""
async with aiohttp.ClientSession() as session:
async with session.get(
f"{self.base_url}/users/{user_id}/learning_path",
headers=self.headers
) as response:
return await response.json()

async def update_progress(self, user_id: str, progress_data: Dict) -> Dict[str, Any]:
"""Updates learning progress and gets recommendations"""
async with aiohttp.ClientSession() as session:
async with session.post(
f"{self.base_url}/users/{user_id}/progress",
headers=self.headers,
json=progress_data
) as response:
return await response.json()

83 changes: 83 additions & 0 deletions backend/app/services/survey.py
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to be adapted to our usecase.

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from typing import List, Dict
from app.models.survey import SurveyQuestion, SurveyOption

class SurveyService:
def __init__(self):
self.questions = [
SurveyQuestion(
id=1,
category="learning_format",
question="What's the best way for you to receive information during learning?",
options=[
SurveyOption(id=1, text="Text-based guide with examples"),
SurveyOption(id=2, text="Video with step-by-step explanations"),
SurveyOption(id=3, text="Practical tasks to solve on your own"),
SurveyOption(id=4, text="Combined format (all options together)")
]
),
SurveyQuestion(
id=2,
category="learning_pace",
question="How much time per week can you dedicate to learning?",
options=[
SurveyOption(id=1, text="Less than 3 hours"),
SurveyOption(id=2, text="3-5 hours"),
SurveyOption(id=3, text="5-10 hours"),
SurveyOption(id=4, text="More than 10 hours")
]
),
SurveyQuestion(
id=3,
category="learning_style",
question="What learning pace do you prefer?",
options=[
SurveyOption(id=1, text="Fast-paced (intensive material coverage)"),
SurveyOption(id=2, text="Balanced (mix of learning and breaks)"),
SurveyOption(id=3, text="Slow-paced (step-by-step learning)")
]
),
SurveyQuestion(
id=4,
category="theory_practice",
question="How do you prefer to learn new concepts?",
options=[
SurveyOption(id=1, text="Read theory first, then practice"),
SurveyOption(id=2, text="Learn by doing, reference theory as needed")
]
)
]

def get_all_questions(self) -> List[SurveyQuestion]:
"""Retrieves all survey questions"""
return self.questions

def get_questions_by_category(self, category: str) -> List[SurveyQuestion]:
"""Retrieves questions filtered by category"""
return [q for q in self.questions if q.category == category]

def validate_answer(self, question_id: int, option_id: int) -> bool:
"""Validates if the answer option exists for the given question"""
question = next((q for q in self.questions if q.id == question_id), None)
if not question:
return False
return any(opt.id == option_id for opt in question.options)

def process_answers(self, answers: Dict) -> Dict:
"""Processes survey answers and formats data for Mem0.ai storage"""
processed_data = {
"learning_preferences": {},
"metadata": {
"timestamp": "2024-01-01T00:00:00Z",
"survey_version": "1.0"
}
}

for answer in answers["answers"]:
question = next(q for q in self.questions if q.id == answer["question_id"])
option = next(opt for opt in question.options if opt.id == answer["selected_option_id"])
processed_data["learning_preferences"][question.category] = {
"selected_option": option.text,
"question_id": question.id
}

return processed_data
28 changes: 28 additions & 0 deletions backend/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# config.py
from pydantic_settings import BaseSettings
from functools import lru_cache

class Settings(BaseSettings):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate classes for different configs will be good.

# Mem0.ai settings
MEM0_API_KEY: str
MEM0_API_URL: str = "https://api.mem0.ai/v1"

# Database settings (if needed later)
DATABASE_URL: str = "sqlite:///./test.db"

# API settings
API_V1_PREFIX: str = "/api/v1"
PROJECT_NAME: str = "AI Tutor"

# Security settings
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WHat is the use of this code

SECRET_KEY: str
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30

class Config:
env_file = ".env"
case_sensitive = True

@lru_cache()
def get_settings():
return Settings()
Binary file added backend/requirements.txt
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verify this

Binary file not shown.