From cc363e7a6d703f1f6d688854dd567f6aac037f99 Mon Sep 17 00:00:00 2001 From: saidsurucu Date: Thu, 17 Jul 2025 20:44:09 +0300 Subject: [PATCH] =?UTF-8?q?unify=20say=C4=B1=C5=9Ftay=20tools?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcp_server_main.py | 330 ++++++++++++++------------ sayistay_mcp_module/models.py | 68 +++++- sayistay_mcp_module/unified_client.py | 137 +++++++++++ 3 files changed, 379 insertions(+), 156 deletions(-) create mode 100644 sayistay_mcp_module/unified_client.py diff --git a/mcp_server_main.py b/mcp_server_main.py index b600ea9..c98eb52 100644 --- a/mcp_server_main.py +++ b/mcp_server_main.py @@ -299,9 +299,12 @@ from sayistay_mcp_module.models import ( GenelKurulSearchRequest, GenelKurulSearchResponse, TemyizKuruluSearchRequest, TemyizKuruluSearchResponse, DaireSearchRequest, DaireSearchResponse, - SayistayDocumentMarkdown + SayistayDocumentMarkdown, + SayistayUnifiedSearchRequest, SayistayUnifiedSearchResult, + SayistayUnifiedDocumentMarkdown, SayistayDecisionTypeEnum ) from sayistay_mcp_module.enums import DaireEnum, KamuIdaresiTuruEnum, WebKararKonusuEnum +from sayistay_mcp_module.unified_client import SayistayUnifiedClient # KVKK Module Imports from kvkk_mcp_module.client import KvkkApiClient @@ -1055,6 +1058,7 @@ kik_client_instance = KikApiClient() rekabet_client_instance = RekabetKurumuApiClient() bedesten_client_instance = BedestenApiClient() sayistay_client_instance = SayistayApiClient() +sayistay_unified_client_instance = SayistayUnifiedClient() kvkk_client_instance = KvkkApiClient() @@ -1843,194 +1847,215 @@ async def get_bedesten_document_markdown( # --- MCP Tools for Sayıştay (Turkish Court of Accounts) --- -@app.tool( - description="Search Sayıştay Genel Kurul decisions for audit and accountability regulations", - annotations={ - "readOnlyHint": True, - "openWorldHint": True, - "idempotentHint": True - } -) -async def search_sayistay_genel_kurul( - karar_no: str = Field("", description="Decision number to search for (e.g., '5415')"), - karar_ek: str = Field("", description="Decision appendix number (max 99, e.g., '1')"), - karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), - karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), - karar_tamami: str = Field("", description="Full text search"), - start: int = Field(0, description="Starting record for pagination (0-based)"), - length: int = Field(10, description="Number of records per page (1-100)") -) -> GenelKurulSearchResponse: - """Search Sayıştay General Assembly decisions.""" - logger.info(f"Tool 'search_sayistay_genel_kurul' called with params: karar_no={karar_no}, karar_ek={karar_ek}, date_range={karar_tarih_baslangic}-{karar_tarih_bitis}, content={karar_tamami}") - - try: - search_request = GenelKurulSearchRequest( - karar_no=karar_no, - karar_ek=karar_ek, - karar_tarih_baslangic=karar_tarih_baslangic, - karar_tarih_bitis=karar_tarih_bitis, - karar_tamami=karar_tamami, - start=start, - length=length - ) - return await sayistay_client_instance.search_genel_kurul_decisions(search_request) - except Exception as e: - logger.exception("Error in tool 'search_sayistay_genel_kurul'") - raise +# DEACTIVATED TOOL - Use search_sayistay_unified instead +# @app.tool( +# description="Search Sayıştay Genel Kurul decisions for audit and accountability regulations", +# annotations={ +# "readOnlyHint": True, +# "openWorldHint": True, +# "idempotentHint": True +# } +# ) +# async def search_sayistay_genel_kurul( +# karar_no: str = Field("", description="Decision number to search for (e.g., '5415')"), +# karar_ek: str = Field("", description="Decision appendix number (max 99, e.g., '1')"), +# karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), +# karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), +# karar_tamami: str = Field("", description="Full text search"), +# start: int = Field(0, description="Starting record for pagination (0-based)"), +# length: int = Field(10, description="Number of records per page (1-100)") +# ) -> GenelKurulSearchResponse: +# """Search Sayıştay General Assembly decisions.""" +# raise ValueError("This tool is deactivated. Use search_sayistay_unified instead.") + +# DEACTIVATED TOOL - Use search_sayistay_unified instead +# @app.tool( +# description="Search Sayıştay Temyiz Kurulu decisions with chamber filtering and comprehensive criteria", +# annotations={ +# "readOnlyHint": True, +# "openWorldHint": True, +# "idempotentHint": True +# } +# ) +# async def search_sayistay_temyiz_kurulu( +# ilam_dairesi: DaireEnum = Field("ALL", description="Audit chamber selection"), +# yili: str = Field("", description="Year (YYYY)"), +# karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), +# karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), +# kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"), +# ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"), +# dosya_no: str = Field("", description="File number for the case"), +# temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number"), +# temyiz_karar: str = Field("", description="Appeals decision text"), +# web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"), +# start: int = Field(0, description="Starting record for pagination (0-based)"), +# length: int = Field(10, description="Number of records per page (1-100)") +# ) -> TemyizKuruluSearchResponse: +# """Search Sayıştay Appeals Board decisions.""" +# raise ValueError("This tool is deactivated. Use search_sayistay_unified instead.") + +# DEACTIVATED TOOL - Use search_sayistay_unified instead +# @app.tool( +# description="Search Sayıştay Daire decisions with chamber filtering and subject categorization", +# annotations={ +# "readOnlyHint": True, +# "openWorldHint": True, +# "idempotentHint": True +# } +# ) +# async def search_sayistay_daire( +# yargilama_dairesi: DaireEnum = Field("ALL", description="Chamber selection"), +# karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), +# karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), +# ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"), +# kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"), +# hesap_yili: str = Field("", description="Fiscal year"), +# web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"), +# web_karar_metni: str = Field("", description="Decision text search"), +# start: int = Field(0, description="Starting record for pagination (0-based)"), +# length: int = Field(10, description="Number of records per page (1-100)") +# ) -> DaireSearchResponse: +# """Search Sayıştay Chamber decisions.""" +# raise ValueError("This tool is deactivated. Use search_sayistay_unified instead.") + +# DEACTIVATED TOOL - Use get_sayistay_document_unified instead +# @app.tool( +# description="Get Sayıştay Genel Kurul decision document in Markdown format", +# annotations={ +# "readOnlyHint": True, +# "openWorldHint": False, +# "idempotentHint": True +# } +# ) +# async def get_sayistay_genel_kurul_document_markdown( +# decision_id: str = Field(..., description="Decision ID from search_sayistay_genel_kurul results") +# ) -> SayistayDocumentMarkdown: +# """Get Sayıştay General Assembly decision as Markdown.""" +# raise ValueError("This tool is deactivated. Use get_sayistay_document_unified instead.") + +# DEACTIVATED TOOL - Use get_sayistay_document_unified instead +# @app.tool( +# description="Get Sayıştay Temyiz Kurulu decision document in Markdown format", +# annotations={ +# "readOnlyHint": True, +# "openWorldHint": False, +# "idempotentHint": True +# } +# ) +# async def get_sayistay_temyiz_kurulu_document_markdown( +# decision_id: str = Field(..., description="Decision ID from search_sayistay_temyiz_kurulu results") +# ) -> SayistayDocumentMarkdown: +# """Get Sayıştay Appeals Board decision as Markdown.""" +# raise ValueError("This tool is deactivated. Use get_sayistay_document_unified instead.") + +# DEACTIVATED TOOL - Use get_sayistay_document_unified instead +# @app.tool( +# description="Get Sayıştay Daire decision document in Markdown format", +# annotations={ +# "readOnlyHint": True, +# "openWorldHint": False, +# "idempotentHint": True +# } +# ) +# async def get_sayistay_daire_document_markdown( +# decision_id: str = Field(..., description="Decision ID from search_sayistay_daire results") +# ) -> SayistayDocumentMarkdown: +# """Get Sayıştay Chamber decision as Markdown.""" +# raise ValueError("This tool is deactivated. Use get_sayistay_document_unified instead.") + +# --- UNIFIED MCP Tools for Sayıştay (Turkish Court of Accounts) --- @app.tool( - description="Search Sayıştay Temyiz Kurulu decisions with chamber filtering and comprehensive criteria", + description="Search Sayıştay decisions unified across all three decision types (Genel Kurul, Temyiz Kurulu, Daire) with comprehensive filtering", annotations={ "readOnlyHint": True, "openWorldHint": True, "idempotentHint": True } ) -async def search_sayistay_temyiz_kurulu( - ilam_dairesi: DaireEnum = Field("ALL", description="Audit chamber selection"), - yili: str = Field("", description="Year (YYYY)"), - karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), - karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), - kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"), +async def search_sayistay_unified( + decision_type: SayistayDecisionTypeEnum = Field(..., description="Decision type: genel_kurul, temyiz_kurulu, or daire"), + + # Common pagination parameters + start: int = Field(0, ge=0, description="Starting record for pagination (0-based)"), + length: int = Field(10, ge=1, le=100, description="Number of records per page (1-100)"), + + # Common search parameters + karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY format)"), + karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY format)"), + kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public administration type filter"), ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"), - dosya_no: str = Field("", description="File number for the case"), - temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number"), - temyiz_karar: str = Field("", description="Appeals decision text"), - web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"), - start: int = Field(0, description="Starting record for pagination (0-based)"), - length: int = Field(10, description="Number of records per page (1-100)") -) -> TemyizKuruluSearchResponse: - """Search Sayıştay Appeals Board decisions.""" - logger.info(f"Tool 'search_sayistay_temyiz_kurulu' called with params: chamber={ilam_dairesi}, year={yili}, admin_type={kamu_idaresi_turu}, subject={web_karar_konusu}") + web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject category filter"), + + # Genel Kurul specific parameters (ignored for other types) + karar_no: str = Field("", description="Decision number (genel_kurul only)"), + karar_ek: str = Field("", description="Decision appendix number (genel_kurul only)"), + karar_tamami: str = Field("", description="Full text search (genel_kurul only)"), + + # Temyiz Kurulu specific parameters (ignored for other types) + ilam_dairesi: DaireEnum = Field("ALL", description="Audit chamber selection (temyiz_kurulu only)"), + yili: str = Field("", description="Year (YYYY format, temyiz_kurulu only)"), + dosya_no: str = Field("", description="File number (temyiz_kurulu only)"), + temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number (temyiz_kurulu only)"), + temyiz_karar: str = Field("", description="Appeals decision text search (temyiz_kurulu only)"), + + # Daire specific parameters (ignored for other types) + yargilama_dairesi: DaireEnum = Field("ALL", description="Chamber selection (daire only)"), + hesap_yili: str = Field("", description="Account year (daire only)"), + web_karar_metni: str = Field("", description="Decision text search (daire only)") +) -> SayistayUnifiedSearchResult: + """Search Sayıştay decisions across all three decision types with unified interface.""" + logger.info(f"Tool 'search_sayistay_unified' called with decision_type={decision_type}") try: - search_request = TemyizKuruluSearchRequest( - ilam_dairesi=ilam_dairesi, - yili=yili, + search_request = SayistayUnifiedSearchRequest( + decision_type=decision_type, + start=start, + length=length, karar_tarih_baslangic=karar_tarih_baslangic, karar_tarih_bitis=karar_tarih_bitis, kamu_idaresi_turu=kamu_idaresi_turu, ilam_no=ilam_no, + web_karar_konusu=web_karar_konusu, + karar_no=karar_no, + karar_ek=karar_ek, + karar_tamami=karar_tamami, + ilam_dairesi=ilam_dairesi, + yili=yili, dosya_no=dosya_no, temyiz_tutanak_no=temyiz_tutanak_no, temyiz_karar=temyiz_karar, - web_karar_konusu=web_karar_konusu, - start=start, - length=length - ) - return await sayistay_client_instance.search_temyiz_kurulu_decisions(search_request) - except Exception as e: - logger.exception("Error in tool 'search_sayistay_temyiz_kurulu'") - raise - -@app.tool( - description="Search Sayıştay Daire decisions with chamber filtering and subject categorization", - annotations={ - "readOnlyHint": True, - "openWorldHint": True, - "idempotentHint": True - } -) -async def search_sayistay_daire( - yargilama_dairesi: DaireEnum = Field("ALL", description="Chamber selection"), - karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), - karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), - ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"), - kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"), - hesap_yili: str = Field("", description="Fiscal year"), - web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"), - web_karar_metni: str = Field("", description="Decision text search"), - start: int = Field(0, description="Starting record for pagination (0-based)"), - length: int = Field(10, description="Number of records per page (1-100)") -) -> DaireSearchResponse: - """Search Sayıştay Chamber decisions.""" - logger.info(f"Tool 'search_sayistay_daire' called with params: chamber={yargilama_dairesi}, admin_type={kamu_idaresi_turu}, subject={web_karar_konusu}, content={web_karar_metni}") - - try: - search_request = DaireSearchRequest( yargilama_dairesi=yargilama_dairesi, - karar_tarih_baslangic=karar_tarih_baslangic, - karar_tarih_bitis=karar_tarih_bitis, - ilam_no=ilam_no, - kamu_idaresi_turu=kamu_idaresi_turu, hesap_yili=hesap_yili, - web_karar_konusu=web_karar_konusu, - web_karar_metni=web_karar_metni, - start=start, - length=length + web_karar_metni=web_karar_metni ) - return await sayistay_client_instance.search_daire_decisions(search_request) + return await sayistay_unified_client_instance.search_unified(search_request) except Exception as e: - logger.exception("Error in tool 'search_sayistay_daire'") + logger.exception("Error in tool 'search_sayistay_unified'") raise @app.tool( - description="Get Sayıştay Genel Kurul decision document in Markdown format", + description="Get Sayıştay decision document in Markdown format for any decision type", annotations={ "readOnlyHint": True, "openWorldHint": False, "idempotentHint": True } ) -async def get_sayistay_genel_kurul_document_markdown( - decision_id: str = Field(..., description="Decision ID from search_sayistay_genel_kurul results") -) -> SayistayDocumentMarkdown: - """Get Sayıştay General Assembly decision as Markdown.""" - logger.info(f"Tool 'get_sayistay_genel_kurul_document_markdown' called for ID: {decision_id}") +async def get_sayistay_document_unified( + decision_id: str = Field(..., description="Decision ID from search_sayistay_unified results"), + decision_type: SayistayDecisionTypeEnum = Field(..., description="Decision type: genel_kurul, temyiz_kurulu, or daire") +) -> SayistayUnifiedDocumentMarkdown: + """Get Sayıştay decision document as Markdown for any decision type.""" + logger.info(f"Tool 'get_sayistay_document_unified' called for ID: {decision_id}, type: {decision_type}") if not decision_id or not decision_id.strip(): raise ValueError("Decision ID must be a non-empty string.") try: - return await sayistay_client_instance.get_document_as_markdown(decision_id, "genel_kurul") + return await sayistay_unified_client_instance.get_document_unified(decision_id, decision_type) except Exception as e: - logger.exception("Error in tool 'get_sayistay_genel_kurul_document_markdown'") - raise - -@app.tool( - description="Get Sayıştay Temyiz Kurulu decision document in Markdown format", - annotations={ - "readOnlyHint": True, - "openWorldHint": False, - "idempotentHint": True - } -) -async def get_sayistay_temyiz_kurulu_document_markdown( - decision_id: str = Field(..., description="Decision ID from search_sayistay_temyiz_kurulu results") -) -> SayistayDocumentMarkdown: - """Get Sayıştay Appeals Board decision as Markdown.""" - logger.info(f"Tool 'get_sayistay_temyiz_kurulu_document_markdown' called for ID: {decision_id}") - - if not decision_id or not decision_id.strip(): - raise ValueError("Decision ID must be a non-empty string.") - - try: - return await sayistay_client_instance.get_document_as_markdown(decision_id, "temyiz_kurulu") - except Exception as e: - logger.exception("Error in tool 'get_sayistay_temyiz_kurulu_document_markdown'") - raise - -@app.tool( - description="Get Sayıştay Daire decision document in Markdown format", - annotations={ - "readOnlyHint": True, - "openWorldHint": False, - "idempotentHint": True - } -) -async def get_sayistay_daire_document_markdown( - decision_id: str = Field(..., description="Decision ID from search_sayistay_daire results") -) -> SayistayDocumentMarkdown: - """Get Sayıştay Chamber decision as Markdown.""" - logger.info(f"Tool 'get_sayistay_daire_document_markdown' called for ID: {decision_id}") - - if not decision_id or not decision_id.strip(): - raise ValueError("Decision ID must be a non-empty string.") - - try: - return await sayistay_client_instance.get_document_as_markdown(decision_id, "daire") - except Exception as e: - logger.exception("Error in tool 'get_sayistay_daire_document_markdown'") + logger.exception("Error in tool 'get_sayistay_document_unified'") raise # --- Application Shutdown Handling --- @@ -2056,6 +2081,7 @@ def perform_cleanup(): globals().get('rekabet_client_instance'), globals().get('bedesten_client_instance'), globals().get('sayistay_client_instance'), + globals().get('sayistay_unified_client_instance'), globals().get('kvkk_client_instance') ] async def close_all_clients_async(): diff --git a/sayistay_mcp_module/models.py b/sayistay_mcp_module/models.py index e32c842..b73e5d6 100644 --- a/sayistay_mcp_module/models.py +++ b/sayistay_mcp_module/models.py @@ -1,9 +1,16 @@ # sayistay_mcp_module/models.py from pydantic import BaseModel, Field -from typing import Optional, List, Union +from typing import Optional, List, Union, Dict, Any +from enum import Enum from .enums import DaireEnum, KamuIdaresiTuruEnum, WebKararKonusuEnum +# --- Unified Enums --- +class SayistayDecisionTypeEnum(str, Enum): + GENEL_KURUL = "genel_kurul" + TEMYIZ_KURULU = "temyiz_kurulu" + DAIRE = "daire" + # ============================================================================ # Genel Kurul (General Assembly) Models # ============================================================================ @@ -155,6 +162,59 @@ class SayistayDocumentMarkdown(BaseModel): decision_id: str = Field(..., description="Unique decision identifier") decision_type: str = Field(..., description="Value") source_url: str = Field(..., description="Original URL where the document was retrieved") - markdown_content: str = Field("", description="Full decision text converted to Markdown format") - retrieval_date: str = Field("", description="Date when document was retrieved (ISO format)") - error_message: str = Field("", description="Error message if document retrieval failed") \ No newline at end of file + markdown_content: Optional[str] = Field(None, description="Full decision text converted to Markdown format") + retrieval_date: Optional[str] = Field(None, description="Date when document was retrieved (ISO format)") + error_message: Optional[str] = Field(None, description="Error message if document retrieval failed") + +# ============================================================================ +# Unified Models +# ============================================================================ + +class SayistayUnifiedSearchRequest(BaseModel): + """Unified search request for all Sayıştay decision types.""" + decision_type: SayistayDecisionTypeEnum = Field(..., description="Decision type: genel_kurul, temyiz_kurulu, or daire") + + # Common pagination parameters + start: int = Field(0, ge=0, description="Starting record for pagination (0-based)") + length: int = Field(10, ge=1, le=100, description="Number of records per page (1-100)") + + # Common search parameters + karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY format)") + karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY format)") + kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public administration type filter") + ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)") + web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject category filter") + + # Genel Kurul specific parameters (ignored for other types) + karar_no: str = Field("", description="Decision number (genel_kurul only)") + karar_ek: str = Field("", description="Decision appendix number (genel_kurul only)") + karar_tamami: str = Field("", description="Full text search (genel_kurul only)") + + # Temyiz Kurulu specific parameters (ignored for other types) + ilam_dairesi: DaireEnum = Field("ALL", description="Audit chamber selection (temyiz_kurulu only)") + yili: str = Field("", description="Year (YYYY format, temyiz_kurulu only)") + dosya_no: str = Field("", description="File number (temyiz_kurulu only)") + temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number (temyiz_kurulu only)") + temyiz_karar: str = Field("", description="Appeals decision text search (temyiz_kurulu only)") + + # Daire specific parameters (ignored for other types) + yargilama_dairesi: DaireEnum = Field("ALL", description="Chamber selection (daire only)") + hesap_yili: str = Field("", description="Account year (daire only)") + web_karar_metni: str = Field("", description="Decision text search (daire only)") + +class SayistayUnifiedSearchResult(BaseModel): + """Unified search result containing decisions from any Sayıştay decision type.""" + decision_type: SayistayDecisionTypeEnum = Field(..., description="Type of decisions returned") + decisions: List[Dict[str, Any]] = Field(default_factory=list, description="Decision list (structure varies by type)") + total_records: int = Field(0, description="Total number of records found") + total_filtered: int = Field(0, description="Number of records after filtering") + draw: int = Field(1, description="DataTables draw counter") + +class SayistayUnifiedDocumentMarkdown(BaseModel): + """Unified document model for all Sayıştay decision types.""" + decision_type: SayistayDecisionTypeEnum = Field(..., description="Type of document") + decision_id: str = Field(..., description="Decision ID") + source_url: str = Field(..., description="Source URL of the document") + document_data: Dict[str, Any] = Field(default_factory=dict, description="Document content and metadata") + markdown_content: Optional[str] = Field(None, description="Markdown content") + error_message: Optional[str] = Field(None, description="Error message if retrieval failed") \ No newline at end of file diff --git a/sayistay_mcp_module/unified_client.py b/sayistay_mcp_module/unified_client.py new file mode 100644 index 0000000..b1dac65 --- /dev/null +++ b/sayistay_mcp_module/unified_client.py @@ -0,0 +1,137 @@ +# sayistay_mcp_module/unified_client.py +# Unified client for all three Sayıştay decision types + +import logging +from typing import Optional, Dict, Any +from urllib.parse import urlparse + +from .models import ( + SayistayUnifiedSearchRequest, + SayistayUnifiedSearchResult, + SayistayUnifiedDocumentMarkdown, + SayistayDecisionTypeEnum, + GenelKurulSearchRequest, + TemyizKuruluSearchRequest, + DaireSearchRequest +) +from .client import SayistayApiClient + +logger = logging.getLogger(__name__) + +class SayistayUnifiedClient: + """Unified client that handles all three Sayıştay decision types.""" + + def __init__(self, request_timeout: float = 60.0): + self.client = SayistayApiClient(request_timeout) + + async def search_unified(self, params: SayistayUnifiedSearchRequest) -> SayistayUnifiedSearchResult: + """Unified search that routes to appropriate search method based on decision_type.""" + + if params.decision_type == SayistayDecisionTypeEnum.GENEL_KURUL: + # Convert to genel kurul request + genel_kurul_params = GenelKurulSearchRequest( + karar_no=params.karar_no, + karar_ek=params.karar_ek, + karar_tarih_baslangic=params.karar_tarih_baslangic, + karar_tarih_bitis=params.karar_tarih_bitis, + karar_tamami=params.karar_tamami, + start=params.start, + length=params.length + ) + + result = await self.client.search_genel_kurul_decisions(genel_kurul_params) + + # Convert to unified format + decisions_list = [decision.model_dump() for decision in result.decisions] + + return SayistayUnifiedSearchResult( + decision_type=SayistayDecisionTypeEnum.GENEL_KURUL, + decisions=decisions_list, + total_records=result.total_records, + total_filtered=result.total_filtered, + draw=result.draw + ) + + elif params.decision_type == SayistayDecisionTypeEnum.TEMYIZ_KURULU: + # Convert to temyiz kurulu request + temyiz_params = TemyizKuruluSearchRequest( + ilam_dairesi=params.ilam_dairesi, + yili=params.yili, + karar_tarih_baslangic=params.karar_tarih_baslangic, + karar_tarih_bitis=params.karar_tarih_bitis, + kamu_idaresi_turu=params.kamu_idaresi_turu, + ilam_no=params.ilam_no, + dosya_no=params.dosya_no, + temyiz_tutanak_no=params.temyiz_tutanak_no, + temyiz_karar=params.temyiz_karar, + web_karar_konusu=params.web_karar_konusu, + start=params.start, + length=params.length + ) + + result = await self.client.search_temyiz_kurulu_decisions(temyiz_params) + + # Convert to unified format + decisions_list = [decision.model_dump() for decision in result.decisions] + + return SayistayUnifiedSearchResult( + decision_type=SayistayDecisionTypeEnum.TEMYIZ_KURULU, + decisions=decisions_list, + total_records=result.total_records, + total_filtered=result.total_filtered, + draw=result.draw + ) + + elif params.decision_type == SayistayDecisionTypeEnum.DAIRE: + # Convert to daire request + daire_params = DaireSearchRequest( + yargilama_dairesi=params.yargilama_dairesi, + karar_tarih_baslangic=params.karar_tarih_baslangic, + karar_tarih_bitis=params.karar_tarih_bitis, + ilam_no=params.ilam_no, + kamu_idaresi_turu=params.kamu_idaresi_turu, + hesap_yili=params.hesap_yili, + web_karar_konusu=params.web_karar_konusu, + web_karar_metni=params.web_karar_metni, + start=params.start, + length=params.length + ) + + result = await self.client.search_daire_decisions(daire_params) + + # Convert to unified format + decisions_list = [decision.model_dump() for decision in result.decisions] + + return SayistayUnifiedSearchResult( + decision_type=SayistayDecisionTypeEnum.DAIRE, + decisions=decisions_list, + total_records=result.total_records, + total_filtered=result.total_filtered, + draw=result.draw + ) + + else: + raise ValueError(f"Unsupported decision type: {params.decision_type}") + + async def get_document_unified(self, decision_id: str, decision_type: SayistayDecisionTypeEnum) -> SayistayUnifiedDocumentMarkdown: + """Unified document retrieval for all Sayıştay decision types.""" + + # Convert enum to string for backend client + decision_type_str = decision_type.value + + # Use existing client method + result = await self.client.get_document_as_markdown(decision_id, decision_type_str) + + return SayistayUnifiedDocumentMarkdown( + decision_type=decision_type, + decision_id=result.decision_id, + source_url=result.source_url, + document_data=result.model_dump(), + markdown_content=result.markdown_content, + error_message=result.error_message + ) + + async def close_client_session(self): + """Close the underlying client session.""" + if hasattr(self.client, 'close_client_session'): + await self.client.close_client_session() \ No newline at end of file