SHL_Reco_sys / api.py
Saalil's picture
Upload 13 files
1567477 verified
Raw
History Blame Contribute Delete
4.51 kB
"""
FastAPI backend for SHL Assessment Recommendation System
Follows API specification from assignment Appendix 2
"""
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from typing import List, Optional
import sys
import os
# Add src to path
sys.path.insert(0, os.path.dirname(__file__))
from recommendation_engine import RecommendationEngine
# Initialize FastAPI app
app = FastAPI(
title="SHL Assessment Recommendation API",
description="API for recommending SHL assessments based on job descriptions",
version="1.0.0"
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Initialize recommendation engine (singleton)
engine = None
@app.on_event("startup")
async def startup_event():
"""Load recommendation engine on startup"""
global engine
print("Initializing recommendation engine...")
# Use absolute path relative to this file
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
catalog_path = os.path.join(base_dir, 'data', 'shl_catalogue.csv')
engine = RecommendationEngine(catalog_path=catalog_path)
print("API ready!")
# Request/Response Models
class RecommendRequest(BaseModel):
query: str = Field(..., description="Job description or natural language query")
top_k: Optional[int] = Field(10, description="Number of recommendations (1-10)", ge=1, le=10)
class AssessmentRecommendation(BaseModel):
assessment_name: str
url: str
relevance_score: Optional[float] = None
test_type: Optional[str] = None
explanation: Optional[str] = None
class RecommendResponse(BaseModel):
query: str
recommendations: List[AssessmentRecommendation]
total_results: int
explanation: Optional[str] = None
best_recommendation: Optional[str] = None
# Endpoints
@app.get("/health")
async def health_check():
"""
Health check endpoint
Returns API status
"""
return {
"status": "healthy",
"message": "SHL Assessment Recommendation API is running",
"version": "1.0.0"
}
@app.post("/recommend", response_model=RecommendResponse)
async def recommend_assessments(request: RecommendRequest):
"""
Recommend assessments based on job query
Parameters:
- query: Job description or natural language query
- top_k: Number of recommendations to return (1-10)
Returns:
- List of recommended assessments with URLs and relevance scores
"""
if not engine:
raise HTTPException(status_code=503, detail="Recommendation engine not initialized")
if not request.query or len(request.query.strip()) < 10:
raise HTTPException(status_code=400, detail="Query must be at least 10 characters")
try:
# Get recommendations
result = engine.recommend(request.query, top_k=request.top_k)
# Format response
recommendations = [
AssessmentRecommendation(
assessment_name=rec['assessment_name'],
url=rec['url'],
relevance_score=rec['similarity_score'],
test_type=rec['test_type_label'],
explanation=None # Can add per-item explanation if needed
)
for rec in result['recommendations']
]
return RecommendResponse(
query=result['query'],
recommendations=recommendations,
total_results=result['total_results'],
explanation=result['explanation'],
best_recommendation=result['best_recommendation']
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Recommendation error: {str(e)}")
@app.get("/")
async def root():
"""Root endpoint with API information"""
return {
"message": "SHL Assessment Recommendation API",
"endpoints": {
"health": "/health - Check API status",
"recommend": "/recommend - Get assessment recommendations (POST)",
"docs": "/docs - Interactive API documentation"
},
"version": "1.0.0"
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)