search patch
This commit is contained in:
		
							parent
							
								
									86d5e03157
								
							
						
					
					
						commit
						5306640238
					
				
							
								
								
									
										26
									
								
								app/core/models/search_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/core/models/search_models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					from uuid import UUID
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					from typing import Optional, List, Any
 | 
				
			||||||
 | 
					from pydantic import BaseModel, Field
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserSearchResult(BaseModel):
 | 
				
			||||||
 | 
					    user_id: UUID
 | 
				
			||||||
 | 
					    login: str
 | 
				
			||||||
 | 
					    full_name: Optional[str] = None
 | 
				
			||||||
 | 
					    custom_name: Optional[str] = None
 | 
				
			||||||
 | 
					    created_at: datetime = Field(..., description="Дата регистрации")
 | 
				
			||||||
 | 
					    profile: Optional[Any] = Field(None, description="Модель как у /profile/{user_id}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SearchData(BaseModel):
 | 
				
			||||||
 | 
					    users: List[UserSearchResult]
 | 
				
			||||||
 | 
					    groups: List[Any] = []
 | 
				
			||||||
 | 
					    channels: List[Any] = []
 | 
				
			||||||
 | 
					    messages: List[Any] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SearchResponse(BaseModel):
 | 
				
			||||||
 | 
					    status: str
 | 
				
			||||||
 | 
					    data: SearchData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										42
									
								
								app/core/services/search_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/core/services/search_service.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import httpx
 | 
				
			||||||
 | 
					from app.core import config
 | 
				
			||||||
 | 
					from app.core.localizer import localizer
 | 
				
			||||||
 | 
					from app.core.http_client import authorized_get
 | 
				
			||||||
 | 
					from app.core.models.search_models import SearchResponse, SearchData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def search_by_query(login: str, token: str, query: str):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Поиск по строке: пользователи (префикс), далее можно расширить под группы/каналы/сообщения.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :return: tuple (ok: bool, data: SearchData | str)
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    url = f"{config.BASE_URL}/v1/feed/search"
 | 
				
			||||||
 | 
					    headers = {"Authorization": f"Bearer {token}"}
 | 
				
			||||||
 | 
					    params = {"query": query}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        response = await authorized_get(url, login=login, access_token=token, headers=headers, params=params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        print("response.status_code", response.status_code)
 | 
				
			||||||
 | 
					        if response.status_code == 200:
 | 
				
			||||||
 | 
					            data = response.json()
 | 
				
			||||||
 | 
					            if data.get("status") == "fine":
 | 
				
			||||||
 | 
					                model = SearchResponse(**data)
 | 
				
			||||||
 | 
					                return True, model.data
 | 
				
			||||||
 | 
					            return False, data.get("detail", localizer.translate("Произошла ошибка"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if response.status_code in [401, 403]:
 | 
				
			||||||
 | 
					            error_data = response.json()
 | 
				
			||||||
 | 
					            return False, error_data.get("detail", localizer.translate("Недостаточно прав или неавторизован"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if response.status_code == 422:
 | 
				
			||||||
 | 
					            return False, localizer.translate("Некорректные параметры запроса")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return False, f"{localizer.translate('Неожиданный статус')}: {response.status_code}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    except httpx.RequestError as e:
 | 
				
			||||||
 | 
					        return False, f"{localizer.translate('Сетевая ошибка')}: {e}"
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        return False, f"{localizer.translate('Внутренняя ошибка')}: {e}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,6 +20,7 @@ from app.ui.views.chat_view import ChatView
 | 
				
			|||||||
from app.core.services.chat_service import get_chat_history, send_private_message
 | 
					from app.core.services.chat_service import get_chat_history, send_private_message
 | 
				
			||||||
from app.core.models.chat_models import PrivateMessageSendRequest, MessageItem
 | 
					from app.core.models.chat_models import PrivateMessageSendRequest, MessageItem
 | 
				
			||||||
from uuid import UUID
 | 
					from uuid import UUID
 | 
				
			||||||
 | 
					from app.core.services.search_service import search_by_query
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class YobbleHomeView(QWidget):
 | 
					class YobbleHomeView(QWidget):
 | 
				
			||||||
    REQUIRED_PERMISSIONS = {
 | 
					    REQUIRED_PERMISSIONS = {
 | 
				
			||||||
@ -271,6 +272,8 @@ class YobbleHomeView(QWidget):
 | 
				
			|||||||
        self.search_input.setPlaceholderText("Поиск…")
 | 
					        self.search_input.setPlaceholderText("Поиск…")
 | 
				
			||||||
        self.search_input.hide()
 | 
					        self.search_input.hide()
 | 
				
			||||||
        top_bar_layout.addWidget(self.search_input, 1)
 | 
					        top_bar_layout.addWidget(self.search_input, 1)
 | 
				
			||||||
 | 
					        # Запуск поиска по Enter
 | 
				
			||||||
 | 
					        self.search_input.returnPressed.connect(self.on_search_submit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Новые кнопки справа
 | 
					        # Новые кнопки справа
 | 
				
			||||||
        self.search_button = QPushButton("🔍")
 | 
					        self.search_button = QPushButton("🔍")
 | 
				
			||||||
@ -664,6 +667,38 @@ class YobbleHomeView(QWidget):
 | 
				
			|||||||
        self.search_button.show()
 | 
					        self.search_button.show()
 | 
				
			||||||
        self.notification_button.show()
 | 
					        self.notification_button.show()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_search_submit(self):
 | 
				
			||||||
 | 
					        """Обработчик Enter в поле поиска."""
 | 
				
			||||||
 | 
					        query = (self.search_input.text() or "").strip()
 | 
				
			||||||
 | 
					        if len(query) < 1:
 | 
				
			||||||
 | 
					            self.show_notification(localizer.translate("Введите минимум 1 символ"), is_error=True)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        # Запускаем асинхронный поиск
 | 
				
			||||||
 | 
					        asyncio.ensure_future(self._do_search(query))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _do_search(self, query: str):
 | 
				
			||||||
 | 
					        """Вызов серверного поиска и показ краткого результата."""
 | 
				
			||||||
 | 
					        # Получаем текущие учётные данные
 | 
				
			||||||
 | 
					        login = self.username
 | 
				
			||||||
 | 
					        token = await get_current_access_token()
 | 
				
			||||||
 | 
					        if not token:
 | 
				
			||||||
 | 
					            self.show_notification(localizer.translate("Не найден токен авторизации"), is_error=True)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ok, data_or_error = await search_by_query(login, token, query)
 | 
				
			||||||
 | 
					        if not ok:
 | 
				
			||||||
 | 
					            self.show_notification(str(data_or_error), is_error=True)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = data_or_error
 | 
				
			||||||
 | 
					        users_cnt = len(getattr(data, 'users', []) or [])
 | 
				
			||||||
 | 
					        groups_cnt = len(getattr(data, 'groups', []) or [])
 | 
				
			||||||
 | 
					        channels_cnt = len(getattr(data, 'channels', []) or [])
 | 
				
			||||||
 | 
					        messages_cnt = len(getattr(data, 'messages', []) or [])
 | 
				
			||||||
 | 
					        self.show_notification(
 | 
				
			||||||
 | 
					            localizer.translate(f"Найдено: пользователи {users_cnt}, беседы {groups_cnt}, паблики {channels_cnt}, сообщения {messages_cnt}")
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle_notification_click(self):
 | 
					    def handle_notification_click(self):
 | 
				
			||||||
        """Пустышка для кнопки уведомлений."""
 | 
					        """Пустышка для кнопки уведомлений."""
 | 
				
			||||||
        show_themed_messagebox(
 | 
					        show_themed_messagebox(
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user