""" Provider (szolgáltató) Pydantic sémák a crowdsourced partnerkeresőhöz. Tartalmazza a keresési, gyors felvételi és szerkesztési sémákat. P1 CRITICAL ALIGN (2026-06-17): Atomizált címmezők bevezetése. - street -> address_street_name, address_street_type, address_house_number - A ProviderSearchResult is tartalmazza az atomizált címmezőket. 4-Level Category Architecture (2026-06-17): =========================================== - CategoryTreeNode: Hierarchikus fa struktúra a frontend checkboxaihoz - CategoryAutocompleteItem: Szöveges kereső találatok Szint 2 és Szint 3 címkékhez - ProviderUpdateIn: kibővítve category_ids listával a hibrid mentéshez """ from pydantic import BaseModel, Field, ConfigDict from typing import Optional, List # ────────────────────────────────────────────── # 4-LEVEL CATEGORY SCHEMAS (2026-06-17) # ────────────────────────────────────────────── class CategoryTreeNode(BaseModel): """Egy csomópont a kategória hierarchikus fában. Tartalmazza a gyermek csomópontokat rekurzívan. """ id: int key: str name_hu: Optional[str] = None name_en: Optional[str] = None level: int = 0 path: Optional[str] = None is_official: bool = True children: List["CategoryTreeNode"] = Field(default_factory=list) model_config = ConfigDict(from_attributes=True) class CategoryAutocompleteItem(BaseModel): """Egy találat a kategória autocomplete keresőben. Csak Szint 2 (Szakma) és Szint 3 (Specifikus feladat) címkéket listáz, ahol is_official=True. """ id: int key: str name_hu: Optional[str] = None name_en: Optional[str] = None level: int = 0 path: Optional[str] = None parent_id: Optional[int] = None model_config = ConfigDict(from_attributes=True) # ────────────────────────────────────────────── # EXISTING SCHEMAS (extended) # ────────────────────────────────────────────── class ExpertiseCategoryOut(BaseModel): """Expertise kategória kimeneti sémája a frontend dropdown számára. Kibővítve a 4-level hierarchy mezőkkel. """ id: int key: str name_hu: Optional[str] = None name_en: Optional[str] = None category: Optional[str] = None level: int = 0 parent_id: Optional[int] = None path: Optional[str] = None model_config = ConfigDict(from_attributes=True) class CategoryInfo(BaseModel): """Egy kategória rövid adatai a keresési találatokhoz. Tartalmazza a kategória ID-ját, nevét és szintjét, hogy a frontend meg tudja jeleníteni a kapcsolódó szolgáltatásokat a kártyákon. """ id: int name_hu: Optional[str] = None name_en: Optional[str] = None level: int = 0 key: str class ProviderSearchResult(BaseModel): """Egy szolgáltató keresési találatának adatai. P1 CRITICAL ALIGN (2026-06-17): Atomizált címmezők. - address_street_name, address_street_type, address_house_number - A frontend ezeket intelligensen fűzi össze megjelenítéskor. FEATURE (2026-06-17): categories mező. - A szolgáltatóhoz tartozó kategóriák (ExpertiseTag) listája. - Tartalmazza az ID-t, nevet és szintet a frontend chip megjelenítéshez. """ id: int name: str category: Optional[str] = None specialization: List[str] = Field(default_factory=list) categories: List[CategoryInfo] = Field(default_factory=list) city: Optional[str] = None address: Optional[str] = None address_zip: Optional[str] = None address_street_name: Optional[str] = None address_street_type: Optional[str] = None address_house_number: Optional[str] = None plus_code: Optional[str] = None contact_phone: Optional[str] = None contact_email: Optional[str] = None website: Optional[str] = None tags: List[str] = Field(default_factory=list) source: str = Field(..., description="Forrás: verified_org | staged_data | crowd_added") is_verified: bool = False rating: Optional[float] = None model_config = ConfigDict(from_attributes=True) class ProviderSearchResponse(BaseModel): """Keresési eredmények lapozással.""" results: List[ProviderSearchResult] = Field(default_factory=list) total: int = 0 page: int = 1 per_page: int = 20 class ProviderQuickAddIn(BaseModel): """Gyors szolgáltató felvétel bemeneti adatai. P1 CRITICAL ALIGN (2026-06-17): Atomizált címmezők. - street ELTÁVOLÍTVA, helyette address_street_name, address_street_type, address_house_number P0 CRITICAL BUGFIX (2026-06-17): category_id most már opcionális. - A 4-level kategória rendszer bevezetésével a frontend a category_ids tömböt használja a kategóriák kiválasztására, nem a régi category_id-t. - category_id megtartva opcionálisként a backward compatibility miatt. - category_ids: A kiválasztott kategória ID-k listája (Szint 0-3 checkboxokból) - new_tags: A user által gépelt új (még nem létező) címkék listája """ name: str = Field(..., min_length=2, max_length=200, description="Szolgáltató neve") category_id: Optional[int] = Field(None, ge=1, description="Elsődleges kategória ID (opcionális, backward compatibility)") category_ids: Optional[List[int]] = Field(None, description="Kiválasztott kategória ID-k (Szint 0-3 checkboxokból)") new_tags: Optional[List[str]] = Field(None, description="User által gépelt új címkenevek (is_official=False, level=3)") city: Optional[str] = Field(None, max_length=100, description="Város") address_zip: Optional[str] = Field(None, max_length=20, description="Irányítószám") address_street_name: Optional[str] = Field(None, max_length=150, description="Utca neve (pl. Egressy)") address_street_type: Optional[str] = Field(None, max_length=50, description="Közterület jellege (pl. utca, út, tér)") address_house_number: Optional[str] = Field(None, max_length=20, description="Házszám (pl. 4/b)") plus_code: Optional[str] = Field(None, max_length=20, description="Google Plus Code (pl. 8FVC9X8V+XV)") contact_phone: Optional[str] = Field(None, max_length=50, description="Telefonszám") contact_email: Optional[str] = Field(None, max_length=255, description="Email cím") website: Optional[str] = Field(None, max_length=255, description="Weboldal URL") tags: Optional[List[str]] = Field(None, description="Szolgáltatás címkék (specialization_tags)") class ProviderQuickAddResponse(BaseModel): """Gyors szolgáltató felvétel válasza.""" id: int name: str status: str = "pending_verification" message: str = "Szolgáltató sikeresen rögzítve. Ellenőrzés alatt." earned_points: int = 0 class ProviderUpdateIn(BaseModel): """Szolgáltató adatainak szerkesztése (PUT /providers/{id}). P1 CRITICAL ALIGN (2026-06-17): Atomizált címmezők. - street ELTÁVOLÍTVA, helyette address_street_name, address_street_type, address_house_number - Tartalmazza a contact_phone, contact_email, website és tags mezőket is. 4-Level Category (2026-06-17): - category_ids: A kiválasztott kategória ID-k listája (Szint 0-3) - new_tags: A user által gépelt, új (még nem létező) címkék listája """ name: str = Field(..., min_length=2, max_length=200, description="Szolgáltató neve") city: Optional[str] = Field(None, max_length=100, description="Város") address_zip: Optional[str] = Field(None, max_length=20, description="Irányítószám") address_street_name: Optional[str] = Field(None, max_length=150, description="Utca neve (pl. Egressy)") address_street_type: Optional[str] = Field(None, max_length=50, description="Közterület jellege (pl. utca, út, tér)") address_house_number: Optional[str] = Field(None, max_length=20, description="Házszám (pl. 4/b)") plus_code: Optional[str] = Field(None, max_length=20, description="Google Plus Code (pl. 8FVC9X8V+XV)") contact_phone: Optional[str] = Field(None, max_length=50, description="Telefonszám") contact_email: Optional[str] = Field(None, max_length=255, description="Email cím") website: Optional[str] = Field(None, max_length=255, description="Weboldal URL") tags: Optional[List[str]] = Field(None, description="Szolgáltatás címkék (specialization_tags)") category_ids: Optional[List[int]] = Field( None, description="Kiválasztott kategória ID-k (Szint 0-3 checkboxokból)" ) new_tags: Optional[List[str]] = Field( None, description="User által gépelt új címkenevek (is_official=False, level=3)" ) class ProviderUpdateResponse(BaseModel): """Szolgáltató szerkesztés válasza.""" id: int name: str message: str = "Szolgáltató adatai sikeresen frissítve." earned_points: int = 0