from fastapi import APIRouter, HTTPException, Depends, UploadFile, File from pydantic import BaseModel from typing import List, Optional from datetime import datetime import base64 import io from app import verify_token, db_service, skin_model, tone_analyzer router = APIRouter() class AnalysisRequest(BaseModel): image_base64: Optional[str] = None conditions: Optional[List[str]] = [] user_id: str class AnalysisResponse(BaseModel): id: str conditions: List[dict] pins: List[dict] skin_tone: dict recommendations: dict dermatologist_warning: bool disclaimer: str @router.post("/analyze-skin", response_model=AnalysisResponse) async def analyze_skin( request: AnalysisRequest, user_id: str = Depends(verify_token) ): """ Analyze skin from image or manual condition selection """ try: analysis_id = f"analysis_{datetime.utcnow().timestamp()}" # Initialize response response = { 'id': analysis_id, 'conditions': [], 'pins': [], 'skin_tone': {}, 'recommendations': {}, 'dermatologist_warning': False, 'disclaimer': "These results are AI-generated. We recommend consulting a dermatologist or researching your skin condition to ensure accuracy." } # Process image if provided if request.image_base64: # Decode image image_bytes = base64.b64decode(request.image_base64) # Analyze skin conditions analysis = skin_model.analyze_skin(image_bytes) if 'error' in analysis: raise HTTPException(status_code=400, detail=analysis['error']) # Analyze skin tone skin_tone = tone_analyzer.analyze_skin_tone(image_bytes) response['conditions'] = analysis.get('conditions', []) response['pins'] = analysis.get('pins', []) response['skin_tone'] = skin_tone response['dermatologist_warning'] = analysis.get('dermatologist_warning', False) # Delete image after analysis (GDPR compliance) # Image is already in memory, no need to delete elif request.conditions: # Manual condition selection response['conditions'] = [ {'condition': c, 'severity': 'mild', 'confidence': 0} for c in request.conditions ] # Generate recommendations from models.cosmetics_matcher import CosmeticMatcher matcher = CosmeticMatcher() recommendations = matcher.find_matches( skin_tone=response['skin_tone'], conditions=response['conditions'] ) response['recommendations'] = recommendations # Save to database db_service.save_analysis( user_id=user_id, analysis_id=analysis_id, data=response ) return response except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/update-pins") async def update_analysis_pins( analysis_id: str, pins: List[dict], user_id: str = Depends(verify_token) ): """ Update analysis based on user-modified pins """ try: # Get existing analysis analysis = db_service.get_analysis(analysis_id, user_id) if not analysis: raise HTTPException(status_code=404, detail="Analysis not found") # Update pins analysis['pins'] = pins # Extract conditions from pins conditions = [ { 'condition': pin['condition'], 'severity': pin.get('severity', 'mild'), 'confidence': pin.get('confidence', 0), 'position': pin.get('position') } for pin in pins ] analysis['conditions'] = conditions # Regenerate recommendations from models.cosmetics_matcher import CosmeticMatcher matcher = CosmeticMatcher() recommendations = matcher.find_matches( skin_tone=analysis['skin_tone'], conditions=conditions ) analysis['recommendations'] = recommendations analysis['updated_at'] = datetime.utcnow().isoformat() analysis['user_modified'] = True # Save to database db_service.update_analysis(analysis_id, user_id, analysis) # Log privacy action db_service.log_privacy_action({ 'user_id': user_id, 'action': 'analysis_modified', 'details': f'User modified {len(pins)} pins', 'timestamp': datetime.utcnow().isoformat() }) return analysis except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/history") async def get_analysis_history( limit: int = 10, user_id: str = Depends(verify_token) ): """ Get user's analysis history """ try: history = db_service.get_user_analyses(user_id, limit) return {"history": history} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/{analysis_id}") async def get_analysis( analysis_id: str, user_id: str = Depends(verify_token) ): """ Get specific analysis by ID """ try: analysis = db_service.get_analysis(analysis_id, user_id) if not analysis: raise HTTPException(status_code=404, detail="Analysis not found") return analysis except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.delete("/{analysis_id}") async def delete_analysis( analysis_id: str, user_id: str = Depends(verify_token) ): """ Delete analysis (GDPR compliance) """ try: db_service.delete_analysis(analysis_id, user_id) return {"message": "Analysis deleted successfully"} except Exception as e: raise HTTPException(status_code=500, detail=str(e))