""" Ticket models with self-parsing from Jira objects. """ from pydantic import BaseModel from typing import Optional, List from datetime import datetime class Attachment(BaseModel): id: str filename: str mimetype: str size: int # bytes url: str content_base64: Optional[str] = None # populated when include_attachments=true @classmethod def from_jira(cls, att) -> "Attachment": return cls( id=att.id, filename=att.filename, mimetype=att.mimeType, size=att.size, url=att.content, ) class Ticket(BaseModel): key: str summary: str description: Optional[str] = None status: str status_category: Optional[str] = None issue_type: str priority: Optional[str] = None project: str assignee: Optional[str] = None reporter: Optional[str] = None labels: List[str] = [] created: Optional[datetime] = None updated: Optional[datetime] = None url: str parent_key: Optional[str] = None # For subtasks @classmethod def from_jira(cls, issue, base_url: str) -> "Ticket": f = issue.fields status_cat = None if hasattr(f.status, "statusCategory"): status_cat = f.status.statusCategory.name # Get parent key for subtasks parent = None if hasattr(f, "parent") and f.parent: parent = f.parent.key return cls( key=issue.key, summary=f.summary or "", description=f.description, status=f.status.name, status_category=status_cat, issue_type=f.issuetype.name, priority=f.priority.name if f.priority else None, project=f.project.key, assignee=f.assignee.displayName if f.assignee else None, reporter=f.reporter.displayName if f.reporter else None, labels=f.labels or [], created=cls._parse_dt(f.created), updated=cls._parse_dt(f.updated), url=f"{base_url}/browse/{issue.key}", parent_key=parent, ) @staticmethod def _parse_dt(val: Optional[str]) -> Optional[datetime]: if not val: return None try: return datetime.fromisoformat(val.replace("Z", "+00:00")) except ValueError: return None class TicketDetail(Ticket): comments: List[dict] = [] linked_issues: List[str] = [] subtasks: List[str] = [] attachments: List[Attachment] = [] @classmethod def from_jira(cls, issue, base_url: str) -> "TicketDetail": base = Ticket.from_jira(issue, base_url) f = issue.fields comments = [] if hasattr(f, "comment") and f.comment: for c in f.comment.comments: comments.append({ "author": c.author.displayName if hasattr(c, "author") else None, "body": c.body, "created": c.created, }) linked = [] if hasattr(f, "issuelinks") and f.issuelinks: for link in f.issuelinks: if hasattr(link, "outwardIssue"): linked.append(link.outwardIssue.key) if hasattr(link, "inwardIssue"): linked.append(link.inwardIssue.key) subtasks = [] if hasattr(f, "subtasks") and f.subtasks: subtasks = [st.key for st in f.subtasks] attachments = [] if hasattr(f, "attachment") and f.attachment: attachments = [Attachment.from_jira(a) for a in f.attachment] return cls( **base.model_dump(), comments=comments, linked_issues=linked, subtasks=subtasks, attachments=attachments, ) class TicketList(BaseModel): tickets: List[Ticket] total: int page: int page_size: int