Source code for xnatctl.models.progress

"""Progress models for tracking operation status.

Provides dataclasses for upload/download progress and operation summaries.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum


[docs] class OperationPhase(Enum): """Operation phases for progress tracking.""" PREPARING = "preparing" ARCHIVING = "archiving" UPLOADING = "uploading" DOWNLOADING = "downloading" PROCESSING = "processing" COMPLETE = "complete" ERROR = "error"
[docs] @dataclass class Progress: """Base progress information.""" phase: OperationPhase current: int = 0 total: int = 0 message: str = "" success: bool = True errors: list[str] = field(default_factory=list) @property def percent(self) -> float: """Calculate completion percentage.""" if self.total == 0: return 0.0 return (self.current / self.total) * 100 @property def is_complete(self) -> bool: """Check if operation is complete.""" return self.phase == OperationPhase.COMPLETE @property def has_errors(self) -> bool: """Check if operation has errors.""" return len(self.errors) > 0 or self.phase == OperationPhase.ERROR
[docs] @dataclass class UploadProgress(Progress): """Upload-specific progress.""" batch_id: int = 0 bytes_sent: int = 0 total_bytes: int = 0 file_path: str = "" @property def bytes_percent(self) -> float: """Calculate bytes completion percentage.""" if self.total_bytes == 0: return 0.0 return (self.bytes_sent / self.total_bytes) * 100 @property def mb_sent(self) -> float: """Return megabytes sent.""" return self.bytes_sent / (1024 * 1024) @property def total_mb(self) -> float: """Return total megabytes.""" return self.total_bytes / (1024 * 1024)
[docs] @dataclass class DownloadProgress(Progress): """Download-specific progress.""" bytes_received: int = 0 total_bytes: int = 0 file_path: str = "" file_name: str = "" @property def bytes_percent(self) -> float: """Calculate bytes completion percentage.""" if self.total_bytes == 0: return 0.0 return (self.bytes_received / self.total_bytes) * 100 @property def mb_received(self) -> float: """Return megabytes received.""" return self.bytes_received / (1024 * 1024) @property def total_mb(self) -> float: """Return total megabytes.""" return self.total_bytes / (1024 * 1024)
[docs] @dataclass class OperationResult: """Generic operation result.""" success: bool total: int succeeded: int failed: int duration: float errors: list[str] = field(default_factory=list) @property def success_rate(self) -> float: """Calculate success rate percentage.""" if self.total == 0: return 100.0 return (self.succeeded / self.total) * 100
[docs] @dataclass class UploadSummary(OperationResult): """Upload operation summary.""" total_files: int = 0 total_size_mb: float = 0.0 batches_total: int = 0 batches_succeeded: int = 0 batches_failed: int = 0 session_id: str = "" upload_id: str = "" @property def throughput_mbps(self) -> float: """Calculate upload throughput in MB/s.""" if self.duration == 0: return 0.0 return self.total_size_mb / self.duration
[docs] @dataclass class DownloadSummary(OperationResult): """Download operation summary.""" total_files: int = 0 total_size_mb: float = 0.0 output_path: str = "" session_id: str = "" verified: bool = False @property def throughput_mbps(self) -> float: """Calculate download throughput in MB/s.""" if self.duration == 0: return 0.0 return self.total_size_mb / self.duration