""" Утилиты для Ollama Translator GUI Дополнительные функции: история, batch обработка, статистика """ import json import os from datetime import datetime from pathlib import Path import hashlib class TranslationHistory: """Управление историей переводов""" def __init__(self, history_file="translation_history.json"): self.history_file = history_file self.history = self.load_history() def load_history(self): """Загрузить историю из файла""" if os.path.exists(self.history_file): try: with open(self.history_file, 'r', encoding='utf-8') as f: return json.load(f) except: return [] return [] def save_history(self): """Сохранить историю в файл""" try: with open(self.history_file, 'w', encoding='utf-8') as f: json.dump(self.history, f, ensure_ascii=False, indent=2) except Exception as e: print(f"Ошибка сохранения истории: {e}") def add_translation(self, source_text, translated_text, source_lang="auto", target_lang="ru"): """Добавить перевод в историю""" entry = { "id": hashlib.md5(source_text.encode()).hexdigest()[:8], "timestamp": datetime.now().isoformat(), "source_text": source_text[:500], # Ограничение для экономии места "translated_text": translated_text[:500], "source_lang": source_lang, "target_lang": target_lang, "char_count": len(source_text), "word_count": len(source_text.split()) } self.history.insert(0, entry) # Добавить в начало # Ограничить размер истории (последние 100 записей) if len(self.history) > 100: self.history = self.history[:100] self.save_history() def get_recent(self, limit=10): """Получить последние переводы""" return self.history[:limit] def search(self, query): """Поиск в истории""" results = [] query_lower = query.lower() for entry in self.history: if (query_lower in entry['source_text'].lower() or query_lower in entry['translated_text'].lower()): results.append(entry) return results def clear_history(self): """Очистить историю""" self.history = [] self.save_history() def get_statistics(self): """Получить статистику""" if not self.history: return { "total_translations": 0, "total_chars": 0, "total_words": 0, "avg_chars": 0, "avg_words": 0 } total_chars = sum(entry.get('char_count', 0) for entry in self.history) total_words = sum(entry.get('word_count', 0) for entry in self.history) return { "total_translations": len(self.history), "total_chars": total_chars, "total_words": total_words, "avg_chars": total_chars // len(self.history) if self.history else 0, "avg_words": total_words // len(self.history) if self.history else 0, "first_translation": self.history[-1]['timestamp'] if self.history else None, "last_translation": self.history[0]['timestamp'] if self.history else None } class BatchProcessor: """Batch обработка файлов""" def __init__(self, translate_func): self.translate_func = translate_func self.results = [] def process_files(self, file_paths, output_dir=None): """Обработать несколько файлов""" self.results = [] for file_path in file_paths: try: # Читаем файл with open(file_path, 'r', encoding='utf-8') as f: content = f.read() # Переводим translation = self.translate_func(content) # Определяем путь для сохранения if output_dir: output_path = Path(output_dir) / f"{Path(file_path).stem}_ru{Path(file_path).suffix}" else: output_path = Path(file_path).parent / f"{Path(file_path).stem}_ru{Path(file_path).suffix}" # Сохраняем with open(output_path, 'w', encoding='utf-8') as f: f.write(translation) self.results.append({ "file": file_path, "status": "success", "output": str(output_path), "chars": len(content) }) except Exception as e: self.results.append({ "file": file_path, "status": "error", "error": str(e) }) return self.results def get_summary(self): """Получить сводку по обработке""" success = sum(1 for r in self.results if r['status'] == 'success') failed = sum(1 for r in self.results if r['status'] == 'error') total_chars = sum(r.get('chars', 0) for r in self.results if r['status'] == 'success') return { "total": len(self.results), "success": success, "failed": failed, "total_chars": total_chars } class FileFormatHandler: """Обработка различных форматов файлов""" @staticmethod def read_file(file_path): """Прочитать файл с автоопределением кодировки""" encodings = ['utf-8', 'cp1251', 'latin-1', 'utf-16'] for encoding in encodings: try: with open(file_path, 'r', encoding=encoding) as f: return f.read() except UnicodeDecodeError: continue raise ValueError(f"Не удалось определить кодировку файла: {file_path}") @staticmethod def save_file(file_path, content, encoding='utf-8'): """Сохранить файл""" with open(file_path, 'w', encoding=encoding) as f: f.write(content) @staticmethod def get_supported_formats(): """Получить список поддерживаемых форматов""" return [ ("Текстовые файлы", "*.txt"), ("Markdown файлы", "*.md"), ("Python файлы", "*.py"), ("JavaScript файлы", "*.js"), ("HTML файлы", "*.html"), ("CSS файлы", "*.css"), ("JSON файлы", "*.json"), ("XML файлы", "*.xml"), ("Все файлы", "*.*") ] class Settings: """Управление настройками приложения""" def __init__(self, settings_file="settings.json"): self.settings_file = settings_file self.default_settings = { "theme": "dark", "font_size": 13, "auto_save": False, "save_history": True, "max_history": 100, "timeout": 120, "model": "translator", "window_width": 1000, "window_height": 700 } self.settings = self.load_settings() def load_settings(self): """Загрузить настройки""" if os.path.exists(self.settings_file): try: with open(self.settings_file, 'r', encoding='utf-8') as f: loaded = json.load(f) # Объединить с дефолтными настройками return {**self.default_settings, **loaded} except: return self.default_settings.copy() return self.default_settings.copy() def save_settings(self): """Сохранить настройки""" try: with open(self.settings_file, 'w', encoding='utf-8') as f: json.dump(self.settings, f, indent=2) except Exception as e: print(f"Ошибка сохранения настроек: {e}") def get(self, key, default=None): """Получить значение настройки""" return self.settings.get(key, default) def set(self, key, value): """Установить значение настройки""" self.settings[key] = value self.save_settings() def reset(self): """Сбросить настройки к дефолтным""" self.settings = self.default_settings.copy() self.save_settings() def format_timestamp(iso_timestamp): """Форматировать timestamp для отображения""" try: dt = datetime.fromisoformat(iso_timestamp) return dt.strftime("%d.%m.%Y %H:%M") except: return iso_timestamp def format_file_size(size_bytes): """Форматировать размер файла""" for unit in ['Б', 'КБ', 'МБ', 'ГБ']: if size_bytes < 1024.0: return f"{size_bytes:.1f} {unit}" size_bytes /= 1024.0 return f"{size_bytes:.1f} ТБ" def truncate_text(text, max_length=100): """Обрезать текст с многоточием""" if len(text) <= max_length: return text return text[:max_length-3] + "..." if __name__ == "__main__": # Тестирование утилит print("=== Тест TranslationHistory ===") history = TranslationHistory("test_history.json") history.add_translation("Hello world", "Привет мир") history.add_translation("Good morning", "Доброе утро") print(f"История: {len(history.history)} записей") print(f"Статистика: {history.get_statistics()}") print("\n=== Тест Settings ===") settings = Settings("test_settings.json") print(f"Тема: {settings.get('theme')}") settings.set('theme', 'light') print(f"Новая тема: {settings.get('theme')}") print("\n=== Тест форматирования ===") print(f"Timestamp: {format_timestamp(datetime.now().isoformat())}") print(f"Размер: {format_file_size(1024*1024*5.5)}") print(f"Текст: {truncate_text('A' * 150, 50)}")