Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
- Replace deprecated datetime.utcnow() with datetime.now(timezone.utc) across all domain models, services, infrastructure, and test files - Add missing timezone imports to all affected files - Fix pytest.ini configuration format from [tool:pytest] to [pytest] - Remove warning suppressions to expose actual issues - Ensure proper pytest marker registration for smoke tests Results: - 305 passed, 2 skipped, 0 warnings (down from 111 warnings) - All functionality preserved with modern datetime API usage - Improved code quality by addressing root causes vs suppression 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
116 lines
3.5 KiB
Python
116 lines
3.5 KiB
Python
"""
|
|
Issue domain models.
|
|
|
|
Contains core business entities and value objects for issue management.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import List, Optional
|
|
from datetime import datetime, timezone
|
|
from enum import Enum
|
|
|
|
from .exceptions import IssueStateError
|
|
|
|
|
|
class IssueState(Enum):
|
|
"""Issue state enumeration."""
|
|
OPEN = "open"
|
|
CLOSED = "closed"
|
|
IN_PROGRESS = "in_progress"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Label:
|
|
"""Value object representing an issue label."""
|
|
name: str
|
|
color: Optional[str] = None
|
|
description: Optional[str] = None
|
|
|
|
def is_state_label(self) -> bool:
|
|
"""Check if this is a state-related label."""
|
|
return self.name.startswith('status:')
|
|
|
|
def is_priority_label(self) -> bool:
|
|
"""Check if this is a priority-related label."""
|
|
return self.name.startswith('priority:')
|
|
|
|
def is_type_label(self) -> bool:
|
|
"""Check if this is a type-related label."""
|
|
return self.name in ['bug', 'enhancement', 'feature', 'documentation']
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class LabelCategories:
|
|
"""Value object for categorized labels."""
|
|
state_labels: List[str]
|
|
priority_labels: List[str]
|
|
type_labels: List[str]
|
|
other_labels: List[str]
|
|
|
|
|
|
@dataclass
|
|
class Issue:
|
|
"""Issue aggregate root."""
|
|
number: int
|
|
title: str
|
|
state: IssueState
|
|
labels: List[Label]
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
milestone: Optional[str] = None
|
|
assignee: Optional[str] = None
|
|
closed_at: Optional[datetime] = None
|
|
|
|
def categorize_labels(self) -> LabelCategories:
|
|
"""Categorize labels by type - pure domain logic."""
|
|
state_labels = [label.name for label in self.labels if label.is_state_label()]
|
|
priority_labels = [label.name for label in self.labels if label.is_priority_label()]
|
|
type_labels = [label.name for label in self.labels if label.is_type_label()]
|
|
other_labels = [
|
|
label.name for label in self.labels
|
|
if not (label.is_state_label() or label.is_priority_label() or label.is_type_label())
|
|
]
|
|
|
|
return LabelCategories(
|
|
state_labels=state_labels,
|
|
priority_labels=priority_labels,
|
|
type_labels=type_labels,
|
|
other_labels=other_labels
|
|
)
|
|
|
|
def close(self) -> None:
|
|
"""Close the issue - domain business rule."""
|
|
if self.state == IssueState.CLOSED:
|
|
raise IssueStateError(
|
|
"Issue is already closed",
|
|
current_state=self.state.value,
|
|
attempted_state=IssueState.CLOSED.value
|
|
)
|
|
|
|
self.state = IssueState.CLOSED
|
|
self.closed_at = datetime.now(timezone.utc)
|
|
|
|
def reopen(self) -> None:
|
|
"""Reopen the issue - domain business rule."""
|
|
if self.state != IssueState.CLOSED:
|
|
raise IssueStateError(
|
|
"Issue is not closed",
|
|
current_state=self.state.value,
|
|
attempted_state=IssueState.OPEN.value
|
|
)
|
|
|
|
self.state = IssueState.OPEN
|
|
self.closed_at = None
|
|
|
|
def add_label(self, label: Label) -> None:
|
|
"""Add a label to the issue."""
|
|
if label not in self.labels:
|
|
self.labels.append(label)
|
|
|
|
def remove_label(self, label_name: str) -> None:
|
|
"""Remove a label from the issue."""
|
|
self.labels = [label for label in self.labels if label.name != label_name]
|
|
|
|
def has_label(self, label_name: str) -> bool:
|
|
"""Check if issue has a specific label."""
|
|
return any(label.name == label_name for label in self.labels) |