Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| 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 | |
| async def health_check(): | |
| """ | |
| Health check endpoint | |
| Returns API status | |
| """ | |
| return { | |
| "status": "healthy", | |
| "message": "SHL Assessment Recommendation API is running", | |
| "version": "1.0.0" | |
| } | |
| 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)}") | |
| 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) | |