From b04211bed40349b010ca65a89c17113b7aca3e6d Mon Sep 17 00:00:00 2001 From: Ida Date: Thu, 28 May 2026 10:57:30 +0200 Subject: [PATCH] =?UTF-8?q?bugfix=20PRM-04:=20Suchfunktion=20filtert=20zue?= =?UTF-8?q?rst,=20dann=20sortiert=20und=20dann=20paginiert,=20sonst=20fehl?= =?UTF-8?q?erhafte=20r=C3=BCckgaben?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/routes/routeDataPrompts.py | 101 ++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 9 deletions(-) diff --git a/modules/routes/routeDataPrompts.py b/modules/routes/routeDataPrompts.py index 331267b5..406e8d59 100644 --- a/modules/routes/routeDataPrompts.py +++ b/modules/routes/routeDataPrompts.py @@ -1,12 +1,13 @@ # Copyright (c) 2025 Patrick Motsch # All rights reserved. from fastapi import APIRouter, HTTPException, Depends, Body, Path, Request, Query -from typing import List, Dict, Any, Optional +from typing import List, Dict, Any, Optional, Tuple from fastapi import status from fastapi.responses import JSONResponse import logging import json import math +from copy import deepcopy # Import auth module from modules.auth import limiter, getCurrentUser @@ -74,6 +75,46 @@ def get_prompts( paginationParams = applyViewToParams(paginationParams, viewConfig) groupByLevels = effective_group_by_levels(paginationParams, viewConfig) + def _getVisiblePromptSearchFields() -> List[str]: + """Return Prompt fields considered visible/searchable in the table.""" + fields: List[str] = [] + for fieldName, fieldInfo in Prompt.model_fields.items(): + if fieldName == "id": + continue + jsonExtra = fieldInfo.json_schema_extra or {} + if jsonExtra.get("frontend_visible", True) is False: + continue + if jsonExtra.get("system", False): + continue + fields.append(fieldName) + return fields + + visibleSearchFields = _getVisiblePromptSearchFields() + + def _applyPromptSearchOnVisibleFields(rows: List[Dict[str, Any]], searchValue: Any) -> List[Dict[str, Any]]: + """Apply global prompt search only on visible Prompt fields.""" + searchTerm = str(searchValue or "").strip().lower() + if not searchTerm: + return rows + filteredRows: List[Dict[str, Any]] = [] + for row in rows: + for fieldName in visibleSearchFields: + value = row.get(fieldName) + if value is None: + continue + if searchTerm in str(value).lower(): + filteredRows.append(row) + break + return filteredRows + + def _extractSearchAndRemoveFromFilters(filters: Optional[Dict[str, Any]]) -> Tuple[Any, Optional[Dict[str, Any]]]: + """Extract generic search from filters and return remaining filters.""" + if not filters: + return None, None + cleanedFilters = deepcopy(filters) + searchValue = cleanedFilters.pop("search", None) + return searchValue, (cleanedFilters if cleanedFilters else None) + def _promptsToEnrichedDicts(promptItems): dicts = [r.model_dump() if hasattr(r, 'model_dump') else (dict(r) if not isinstance(r, dict) else r) for r in promptItems] enrichRowsWithFkLabels(dicts, Prompt) @@ -115,21 +156,57 @@ def get_prompts( if not groupByLevels: # No grouping: let DB handle pagination directly - result = managementInterface.getAllPrompts(pagination=paginationParams) - if paginationParams and hasattr(result, 'items'): - response: dict = { - "items": _promptsToEnrichedDicts(result.items), + promptSearchValue = None + remainingFilters = paginationParams.filters if paginationParams else None + if paginationParams and paginationParams.filters: + promptSearchValue, remainingFilters = _extractSearchAndRemoveFromFilters(paginationParams.filters) + + # For prompt search, we must filter before page slicing. + if paginationParams and promptSearchValue is not None: + result = managementInterface.getAllPrompts(pagination=None) + allItems = _promptsToEnrichedDicts(result if isinstance(result, list) else (result.items if hasattr(result, 'items') else [])) + allItems = _applyPromptSearchOnVisibleFields(allItems, promptSearchValue) + + if remainingFilters or paginationParams.sort: + from modules.interfaces.interfaceDbManagement import ComponentObjects + comp = ComponentObjects() + comp.setUserContext(currentUser) + if remainingFilters: + allItems = comp._applyFilters(allItems, remainingFilters) + if paginationParams.sort: + allItems = comp._applySorting(allItems, paginationParams.sort) + + totalItems = len(allItems) + totalPages = math.ceil(totalItems / paginationParams.pageSize) if totalItems > 0 else 0 + startIdx = (paginationParams.page - 1) * paginationParams.pageSize + endIdx = startIdx + paginationParams.pageSize + response = { + "items": allItems[startIdx:endIdx], "pagination": PaginationMetadata( currentPage=paginationParams.page, pageSize=paginationParams.pageSize, - totalItems=result.totalItems, - totalPages=result.totalPages, + totalItems=totalItems, + totalPages=totalPages, sort=paginationParams.sort, filters=paginationParams.filters ).model_dump(), } else: - response = {"items": _promptsToEnrichedDicts(result if isinstance(result, list) else [result]), "pagination": None} + result = managementInterface.getAllPrompts(pagination=paginationParams) + if paginationParams and hasattr(result, 'items'): + response = { + "items": _promptsToEnrichedDicts(result.items), + "pagination": PaginationMetadata( + currentPage=paginationParams.page, + pageSize=paginationParams.pageSize, + totalItems=result.totalItems, + totalPages=result.totalPages, + sort=paginationParams.sort, + filters=paginationParams.filters + ).model_dump(), + } + else: + response = {"items": _promptsToEnrichedDicts(result if isinstance(result, list) else [result]), "pagination": None} if viewMeta: response["appliedView"] = viewMeta.model_dump() return response @@ -148,8 +225,14 @@ def get_prompts( from modules.interfaces.interfaceDbManagement import ComponentObjects comp = ComponentObjects() comp.setUserContext(currentUser) + filtersForGenericApply = paginationParams.filters + promptSearchValue = None if paginationParams.filters: - allItems = comp._applyFilters(allItems, paginationParams.filters) + promptSearchValue, filtersForGenericApply = _extractSearchAndRemoveFromFilters(paginationParams.filters) + if promptSearchValue is not None: + allItems = _applyPromptSearchOnVisibleFields(allItems, promptSearchValue) + if filtersForGenericApply: + allItems = comp._applyFilters(allItems, filtersForGenericApply) if paginationParams.sort: allItems = comp._applySorting(allItems, paginationParams.sort)