gateway/modules/routes/routeDataPrompts.py

221 lines
No EOL
7.8 KiB
Python

# 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 fastapi import status
import logging
import json
# Import auth module
from modules.auth import limiter, getCurrentUser
# Import interfaces
import modules.interfaces.interfaceDbManagement as interfaceDbManagement
from modules.datamodels.datamodelUtils import Prompt
from modules.datamodels.datamodelUam import User
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResponse, PaginationMetadata, normalize_pagination_dict
from modules.shared.i18nRegistry import apiRouteContext
routeApiMsg = apiRouteContext("routeDataPrompts")
# Configure logger
logger = logging.getLogger(__name__)
# Create router for prompt endpoints
router = APIRouter(
prefix="/api/prompts",
tags=["Manage Prompts"],
responses={404: {"description": "Not found"}}
)
@router.get("")
@limiter.limit("30/minute")
def get_prompts(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
mode: Optional[str] = Query(None, description="'filterValues' for distinct column values, 'ids' for all filtered IDs"),
column: Optional[str] = Query(None, description="Column key (required when mode=filterValues)"),
currentUser: User = Depends(getCurrentUser)
):
"""
Get prompts with optional pagination, sorting, and filtering.
Modes:
- None: paginated list (default)
- filterValues: distinct values for a column (cross-filtered)
- ids: all IDs matching current filters
"""
from modules.routes.routeHelpers import (
handleFilterValuesInMemory, handleIdsInMemory, enrichRowsWithFkLabels,
handleGroupingInRequest, applyGroupScopeFilter,
)
from modules.interfaces.interfaceDbApp import getInterface as getAppInterface
CONTEXT_KEY = "prompts"
# Parse pagination params early — needed for grouping in all modes
paginationParams = None
if pagination:
try:
paginationDict = json.loads(pagination)
if paginationDict:
paginationDict = normalize_pagination_dict(paginationDict)
paginationParams = PaginationParams(**paginationDict)
except (json.JSONDecodeError, ValueError) as e:
raise HTTPException(status_code=400, detail=f"Invalid pagination parameter: {str(e)}")
appInterface = getAppInterface(currentUser)
groupCtx = handleGroupingInRequest(paginationParams, appInterface, CONTEXT_KEY)
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)
return dicts
managementInterface = interfaceDbManagement.getInterface(currentUser)
if mode == "filterValues":
if not column:
raise HTTPException(status_code=400, detail="column parameter required for mode=filterValues")
result = managementInterface.getAllPrompts(pagination=None)
items = _promptsToEnrichedDicts(result)
items = applyGroupScopeFilter(items, groupCtx.itemIds)
return handleFilterValuesInMemory(items, column, pagination)
if mode == "ids":
result = managementInterface.getAllPrompts(pagination=None)
items = _promptsToEnrichedDicts(result)
items = applyGroupScopeFilter(items, groupCtx.itemIds)
return handleIdsInMemory(items, pagination)
result = managementInterface.getAllPrompts(pagination=paginationParams)
if paginationParams:
items = applyGroupScopeFilter(_promptsToEnrichedDicts(result.items), groupCtx.itemIds)
return {
"items": items,
"pagination": PaginationMetadata(
currentPage=paginationParams.page,
pageSize=paginationParams.pageSize,
totalItems=result.totalItems,
totalPages=result.totalPages,
sort=paginationParams.sort,
filters=paginationParams.filters
).model_dump(),
"groupTree": groupCtx.groupTree,
}
else:
items = applyGroupScopeFilter(_promptsToEnrichedDicts(result), groupCtx.itemIds)
return {
"items": items,
"pagination": None,
"groupTree": groupCtx.groupTree,
}
@router.post("", response_model=Prompt)
@limiter.limit("10/minute")
def create_prompt(
request: Request,
prompt: Prompt,
currentUser: User = Depends(getCurrentUser)
) -> Prompt:
"""Create a new prompt"""
managementInterface = interfaceDbManagement.getInterface(currentUser)
# Create prompt
newPrompt = managementInterface.createPrompt(prompt)
return Prompt(**newPrompt)
@router.get("/{promptId}", response_model=Prompt)
@limiter.limit("30/minute")
def get_prompt(
request: Request,
promptId: str = Path(..., description="ID of the prompt"),
currentUser: User = Depends(getCurrentUser)
) -> Prompt:
"""Get a specific prompt"""
managementInterface = interfaceDbManagement.getInterface(currentUser)
# Get prompt
prompt = managementInterface.getPrompt(promptId)
if not prompt:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Prompt with ID {promptId} not found"
)
return prompt
@router.put("/{promptId}", response_model=Prompt)
@limiter.limit("10/minute")
def update_prompt(
request: Request,
promptId: str = Path(..., description="ID of the prompt to update"),
promptData: Dict[str, Any] = Body(...),
currentUser: User = Depends(getCurrentUser)
) -> Prompt:
"""Update an existing prompt (supports partial updates for inline editing)"""
managementInterface = interfaceDbManagement.getInterface(currentUser)
# Check if the prompt exists
existingPrompt = managementInterface.getPrompt(promptId)
if not existingPrompt:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Prompt with ID {promptId} not found"
)
# Remove id from update data if present
update_data = {k: v for k, v in promptData.items() if k != "id"}
# Update prompt (ownership check happens in interface)
try:
updatedPrompt = managementInterface.updatePrompt(promptId, update_data)
except PermissionError as e:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=str(e)
)
if not updatedPrompt:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=routeApiMsg("Error updating the prompt")
)
return Prompt(**updatedPrompt)
@router.delete("/{promptId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
def delete_prompt(
request: Request,
promptId: str = Path(..., description="ID of the prompt to delete"),
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
"""Delete a prompt"""
managementInterface = interfaceDbManagement.getInterface(currentUser)
# Check if the prompt exists
existingPrompt = managementInterface.getPrompt(promptId)
if not existingPrompt:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Prompt with ID {promptId} not found"
)
try:
success = managementInterface.deletePrompt(promptId)
except PermissionError as e:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=str(e)
)
if not success:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=routeApiMsg("Error deleting the prompt")
)
return {"message": f"Prompt with ID {promptId} successfully deleted"}