from fastapi import FastAPI, HTTPException from pydantic import BaseModel, HttpUrl, Field from typing import List, Dict, Optional, Tuple, Any import requests from helpers.text_blocks import extract_text_from_url from helpers.output_clipper import clip_by_ranges from parser.assembler import parse_law_from_texts from supabase_utils import save_law_to_supabase # استدعاء الدالة لحفظ القانون app = FastAPI( title="Text Extractor API", description="API لاستخراج النصوص من صفحات الويب مع إمكانية التحكم في النطاقات", version="2.1.0" ) # ----------------------------- # نماذج البيانات # ----------------------------- class URLRequest(BaseModel): url: HttpUrl return_parsed: bool = Field( default=False, description="إذا True → إرجاع المستند القانوني المحلل، إذا False → إرجاع النصوص الخام" ) save_to_supabase: bool = Field( default=False, description="إذا True → حفظ المستند القانوني في قاعدة البيانات" ) timeout: int = Field(default=10, ge=1, le=60) ranges: Optional[List[List[int]]] = Field( default=None, description="نطاقات الاستخراج في شكل [[start, end], [start, end]] - اختياري" ) class TextResponse(BaseModel): text: str class CountResponse(BaseModel): total_texts: int url: str class LegalDocumentResponse(BaseModel): raw_texts: Optional[List[TextResponse]] = None parsed_document: Optional[Dict[str, Any]] = None # ----------------------------- # التحقق من صحة النطاقات # ----------------------------- def validate_ranges(ranges: List[List[int]]) -> List[Tuple[int, int]]: validated_ranges = [] for i, range_pair in enumerate(ranges): if len(range_pair) != 2: raise HTTPException( status_code=400, detail=f"النطاق رقم {i+1} غير صحيح. كل نطاق يجب أن يحتوي على عنصرين: [start, end]" ) start, end = range_pair if not isinstance(start, int) or not isinstance(end, int): raise HTTPException( status_code=400, detail=f"النطاق رقم {i+1} غير صحيح. القيم يجب أن تكون أرقام صحيحة" ) if start < 0 or end <= start: raise HTTPException( status_code=400, detail=f"النطاق رقم {i+1} غير صحيح. النهاية يجب أن تكون أكبر من البداية والبداية >= 0" ) validated_ranges.append((start, end)) return validated_ranges # ----------------------------- # نقطة النهاية لاستخراج النصوص / القانون مع حفظ قاعدة البيانات # ----------------------------- @app.post("/extract", response_model=LegalDocumentResponse) async def extract_text_endpoint(request: URLRequest): try: # 1) استخراج جميع النصوص (قائمة قواميس) all_texts = extract_text_from_url(str(request.url), request.timeout) # 2) تطبيق النطاقات إذا وجدت if request.ranges: validated_ranges = validate_ranges(request.ranges) filtered_texts = clip_by_ranges(all_texts, validated_ranges) else: filtered_texts = all_texts # 3) إذا طلب تحليل القانون if request.return_parsed: parsed_document = parse_law_from_texts(filtered_texts) # 4) حفظ القانون في Supabase إذا كان save_to_supabase = True if request.save_to_supabase: save_law_to_supabase(parsed_document["law"]) parsed_document["saved_to_db"] = True # تحديث المفتاح داخل الاستجابة else: parsed_document["saved_to_db"] = False return LegalDocumentResponse(parsed_document=parsed_document) # 5) خلاف ذلك: إرجاع النصوص الخام return LegalDocumentResponse(raw_texts=filtered_texts) except requests.RequestException as e: raise HTTPException(status_code=400, detail=f"خطأ في جلب الصفحة: {str(e)}") except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"خطأ في معالجة المحتوى: {str(e)}")