bugfix PRM-04: Suchfunktion filtert zuerst, dann sortiert und dann paginiert, sonst fehlerhafte rückgaben

This commit is contained in:
Ida 2026-05-28 10:57:30 +02:00
parent 8c2e9d2183
commit b04211bed4

View file

@ -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)