Files
defrag/app/shared/models.py
2025-12-13 11:56:06 +01:00

128 lines
3.7 KiB
Python

"""Data models for the disk reorganizer"""
from dataclasses import dataclass, field
from pathlib import Path
from datetime import datetime
from typing import Optional
@dataclass
class FileRecord:
"""Core file record with all metadata"""
path: Path
size: int
modified_time: float
created_time: float
disk_label: str
checksum: Optional[str] = None
status: str = 'indexed' # indexed, planned, moved, verified
category: Optional[str] = None
duplicate_of: Optional[str] = None
def to_dict(self) -> dict:
"""Convert to dictionary for serialization"""
return {
'path': str(self.path),
'size': self.size,
'modified_time': self.modified_time,
'created_time': self.created_time,
'disk_label': self.disk_label,
'checksum': self.checksum,
'status': self.status,
'category': self.category,
'duplicate_of': self.duplicate_of
}
@dataclass
class OperationRecord:
"""Record of a migration operation"""
source_path: Path
target_path: Path
operation_type: str # move, copy, hardlink, symlink
size: int = 0
status: str = 'pending' # pending, in_progress, completed, failed
error: Optional[str] = None
executed_at: Optional[datetime] = None
verified: bool = False
def to_dict(self) -> dict:
"""Convert to dictionary for serialization"""
return {
'source_path': str(self.source_path),
'target_path': str(self.target_path),
'operation_type': self.operation_type,
'size': self.size,
'status': self.status,
'error': self.error,
'executed_at': self.executed_at.isoformat() if self.executed_at else None,
'verified': self.verified
}
@dataclass
class DiskInfo:
"""Information about a disk/volume"""
name: str
device: str
mount_point: Path
total_size: int
used_size: int
free_size: int
fs_type: str
@property
def usage_percent(self) -> float:
"""Calculate usage percentage"""
if self.total_size == 0:
return 0.0
return (self.used_size / self.total_size) * 100
@dataclass
class MigrationPlan:
"""Complete migration plan"""
target_disk: str
destination_disks: list[str]
operations: list[OperationRecord]
total_size: int
file_count: int
created_at: datetime = field(default_factory=datetime.now)
def to_dict(self) -> dict:
"""Convert to dictionary for serialization"""
return {
'target_disk': self.target_disk,
'destination_disks': self.destination_disks,
'operations': [op.to_dict() for op in self.operations],
'total_size': self.total_size,
'file_count': self.file_count,
'created_at': self.created_at.isoformat()
}
@dataclass
class ProcessingStats:
"""Statistics for processing operations"""
files_processed: int = 0
bytes_processed: int = 0
files_succeeded: int = 0
files_failed: int = 0
start_time: datetime = field(default_factory=datetime.now)
@property
def elapsed_seconds(self) -> float:
"""Calculate elapsed time in seconds"""
return (datetime.now() - self.start_time).total_seconds()
@property
def files_per_second(self) -> float:
"""Calculate processing rate"""
elapsed = self.elapsed_seconds
return self.files_processed / elapsed if elapsed > 0 else 0.0
@property
def bytes_per_second(self) -> float:
"""Calculate throughput"""
elapsed = self.elapsed_seconds
return self.bytes_processed / elapsed if elapsed > 0 else 0.0