| 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()}" |
| |
| |
| 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." |
| } |
| |
| |
| if request.image_base64: |
| |
| image_bytes = base64.b64decode(request.image_base64) |
| |
| |
| analysis = skin_model.analyze_skin(image_bytes) |
| |
| if 'error' in analysis: |
| raise HTTPException(status_code=400, detail=analysis['error']) |
| |
| |
| 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) |
| |
| |
| |
| |
| elif request.conditions: |
| |
| response['conditions'] = [ |
| {'condition': c, 'severity': 'mild', 'confidence': 0} |
| for c in request.conditions |
| ] |
| |
| |
| from models.cosmetics_matcher import CosmeticMatcher |
| matcher = CosmeticMatcher() |
| |
| recommendations = matcher.find_matches( |
| skin_tone=response['skin_tone'], |
| conditions=response['conditions'] |
| ) |
| |
| response['recommendations'] = recommendations |
| |
| |
| 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: |
| |
| analysis = db_service.get_analysis(analysis_id, user_id) |
| |
| if not analysis: |
| raise HTTPException(status_code=404, detail="Analysis not found") |
| |
| |
| analysis['pins'] = 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 |
| |
| |
| 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 |
| |
| |
| db_service.update_analysis(analysis_id, user_id, analysis) |
| |
| |
| 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)) |