""" FileService - сервис для работы с файлами """ import shutil from datetime import datetime from pathlib import Path from typing import Optional class FileService: """Сервис для перемещения файлов и ведения логов""" SUPPORTED_EXTENSIONS = {'.cr2', '.dng', '.arw', '.jpg', '.jpeg', '.png', '.raw', '.tiff'} @classmethod def is_photo(cls, file_path: Path) -> bool: """Проверяет, является ли файл фотографией""" return file_path.suffix.lower() in cls.SUPPORTED_EXTENSIONS @classmethod def resolve_conflict(cls, target_path: Path) -> Path: """Разрешает конфликт имён файлов""" if not target_path.exists(): return target_path counter = 1 stem = target_path.stem suffix = target_path.suffix parent = target_path.parent while True: new_name = f"{stem}_{counter}{suffix}" new_path = parent / new_name if not new_path.exists(): return new_path counter += 1 @classmethod def generate_new_filename(cls, object_name: str, timestamp: datetime, original_suffix: str) -> str: """ Генерирует новое имя файла в формате: ИмяОбъекта_ГГГГ-ММ-ДД_ЧЧ-ММ-СС.расширение """ # Очищаем имя объекта от недопустимых символов safe_object_name = "".join(c for c in object_name if c.isalnum() or c in (' ', '-', '_')).strip() safe_object_name = safe_object_name.replace(' ', '_') # Форматируем дату и время date_str = timestamp.strftime("%Y-%m-%d") time_str = timestamp.strftime("%H-%M-%S") return f"{safe_object_name}_{date_str}_{time_str}{original_suffix}" @classmethod def write_object_log(cls, folder: Path, filename: str, camera: str, optics: str, timestamp: Optional[datetime] = None) -> None: """Записывает запись в лог объекта""" if timestamp is None: timestamp = datetime.now() log_file = folder / "ObjectLog.txt" line = f"{timestamp} {camera if camera else 'Unknown'} {optics if optics else 'Unknown'} - {filename}\n" try: with open(log_file, 'a', encoding='utf-8') as f: f.write(line) except Exception as e: print(f"Ошибка записи лога: {e}") @classmethod def move_file(cls, source: Path, target_folder: Path, camera: str, optics: str, object_name: str = None) -> bool: """ Перемещает файл в целевую папку с переименованием Если object_name указан, файл переименовывается в формат: ИмяОбъекта_дата_время.расширение """ if not source.exists(): print(f"Файл не существует: {source}") return False if not cls.is_photo(source): print(f"Неподдерживаемый формат: {source}") return False try: target_folder.mkdir(parents=True, exist_ok=True) # Получаем время создания файла creation_time = datetime.fromtimestamp(source.stat().st_ctime) # Генерируем новое имя файла, если указано имя объекта if object_name: new_filename = cls.generate_new_filename(object_name, creation_time, source.suffix) else: new_filename = source.name # Записываем в лог (сохраняем оригинальное имя в логе для отслеживания) cls.write_object_log(target_folder, f"{source.name} -> {new_filename}", camera, optics, creation_time) # Формируем целевой путь target_path = cls.resolve_conflict(target_folder / new_filename) # Перемещаем файл shutil.move(str(source), str(target_path)) print(f"Файл перемещён и переименован: {source.name} -> {target_path.name}") return True except Exception as e: print(f"Ошибка перемещения файла {source.name}: {e}") return False @classmethod def clear_watch_folder(cls, folder: Path) -> int: """Очищает папку наблюдения""" if not folder.exists(): return 0 count = 0 for file_path in folder.iterdir(): if file_path.is_file() and cls.is_photo(file_path): try: file_path.unlink() count += 1 except Exception as e: print(f"Ошибка удаления {file_path.name}: {e}") return count