82 lines
2.8 KiB
Python
82 lines
2.8 KiB
Python
# Copyright (c) 2026 Patrick Motsch
|
|
# All rights reserved.
|
|
"""Workflow action: list Redmine tickets from the mirror with filters."""
|
|
|
|
import logging
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from modules.datamodels.datamodelChat import ActionResult
|
|
from modules.features.redmine.serviceRedmine import listTickets
|
|
|
|
from ._shared import resolveInstanceContext, ticketToDict
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _normalizeTrackerIds(value: Any) -> Optional[List[int]]:
|
|
if value is None or value == "":
|
|
return None
|
|
if isinstance(value, int):
|
|
return [value]
|
|
if isinstance(value, str):
|
|
value = [v.strip() for v in value.split(",") if v.strip()]
|
|
if isinstance(value, list):
|
|
ids: List[int] = []
|
|
for v in value:
|
|
try:
|
|
ids.append(int(v))
|
|
except (TypeError, ValueError):
|
|
continue
|
|
return ids or None
|
|
return None
|
|
|
|
|
|
async def listTicketsAction(self, parameters: Dict[str, Any]) -> ActionResult:
|
|
"""List Redmine tickets from the local mirror."""
|
|
try:
|
|
user, mandateId, featureInstanceId = resolveInstanceContext(self.services, parameters)
|
|
except ValueError as exc:
|
|
return ActionResult.isFailure(error=str(exc))
|
|
|
|
trackerIds = _normalizeTrackerIds(parameters.get("trackerIds"))
|
|
statusFilter = (parameters.get("status") or "*").lower()
|
|
if statusFilter not in {"*", "open", "closed"}:
|
|
statusFilter = "*"
|
|
updatedFrom = parameters.get("dateFrom") or None
|
|
updatedTo = parameters.get("dateTo") or None
|
|
assignedToId: Optional[int] = None
|
|
if parameters.get("assignedToId") not in (None, ""):
|
|
try:
|
|
assignedToId = int(parameters["assignedToId"])
|
|
except (TypeError, ValueError):
|
|
return ActionResult.isFailure(error="assignedToId must be an int")
|
|
|
|
try:
|
|
tickets = listTickets(
|
|
user, mandateId, featureInstanceId,
|
|
trackerIds=trackerIds,
|
|
statusFilter=statusFilter,
|
|
updatedOnFrom=updatedFrom,
|
|
updatedOnTo=updatedTo,
|
|
assignedToId=assignedToId,
|
|
)
|
|
except Exception as exc:
|
|
logger.exception("redmine.listTickets failed")
|
|
return ActionResult.isFailure(error=f"List tickets failed: {exc}")
|
|
|
|
# AI-friendly pagination: always capped so we don't accidentally feed a
|
|
# 20k-ticket dump into a context window. Callers that need more must
|
|
# paginate via filters.
|
|
limit = 100
|
|
try:
|
|
limit = max(1, min(500, int(parameters.get("limit") or 100)))
|
|
except (TypeError, ValueError):
|
|
limit = 100
|
|
|
|
truncated = tickets[:limit]
|
|
return ActionResult.isSuccess(data={
|
|
"count": len(truncated),
|
|
"totalMatched": len(tickets),
|
|
"truncated": len(tickets) > limit,
|
|
"tickets": [ticketToDict(t) for t in truncated],
|
|
})
|