Created
September 10, 2025 19:25
-
-
Save xeioex/770315e4efc92688fa05b5482b33ddfa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| Anki TXT Cards Creator | |
| Создает объединенные файлы карточек в формате .txt для импорта в Anki | |
| Поддерживает обработку множества JSON файлов через glob patterns | |
| """ | |
| import json | |
| import os | |
| import glob | |
| from typing import List, Dict, Any, Tuple | |
| import argparse | |
| def load_json_data(file_path: str) -> List[Dict[str, Any]]: | |
| """Загружает данные из JSON файла""" | |
| try: | |
| with open(file_path, 'r', encoding='utf-8') as file: | |
| data = json.load(file) | |
| return data | |
| except FileNotFoundError: | |
| print(f"Ошибка: Файл {file_path} не найден") | |
| return [] | |
| except json.JSONDecodeError as e: | |
| print(f"Ошибка при парсинге JSON в файле {file_path}: {e}") | |
| return [] | |
| def create_txt_file(data: List[Dict[str, Any]], output_file: str, delimiter: str = '\t'): | |
| """ | |
| Создает текстовый файл для импорта в Anki в стандартном формате | |
| Формат: ID [TAB] "содержимое" | |
| """ | |
| try: | |
| with open(output_file, 'w', encoding='utf-8', newline='') as file: | |
| # Записываем заголовки в формате Anki | |
| file.write("#separator:tab\n") | |
| file.write("#html:true\n") | |
| for i, item in enumerate(data): | |
| term = item.get('term', '').strip() | |
| definition = item.get('definition', '').strip() | |
| # Очищаем определение от лишних пробелов и переносов | |
| definition = ' '.join(definition.split()) | |
| # Экранируем кавычки в содержимом (двойные кавычки заменяем на "") | |
| term_escaped = term.replace('"', '""') | |
| definition_escaped = definition.replace('"', '""') | |
| # Объединяем термин и определение | |
| content = f"{term_escaped} {definition_escaped}" | |
| # Записываем в формате Anki: ID[TAB]"содержимое" | |
| file.write(f"{i}{delimiter}\"{content}\"\n") | |
| print(f"✅ Создан текстовый файл: {output_file}") | |
| print(f" Карточек: {len(data)}") | |
| print(f" Формат: стандартный Anki с разделителем табуляция") | |
| except Exception as e: | |
| print(f"Ошибка при создании текстового файла: {e}") | |
| def create_txt_file_two_fields(data: List[Dict[str, Any]], output_file: str, delimiter: str = '\t'): | |
| """ | |
| Создает текстовый файл с двумя отдельными полями для импорта в Anki | |
| Формат: "термин" [TAB] "определение" | |
| """ | |
| try: | |
| with open(output_file, 'w', encoding='utf-8', newline='') as file: | |
| # Записываем заголовки в формате Anki | |
| file.write("#separator:tab\n") | |
| file.write("#html:true\n") | |
| for item in data: | |
| term = item.get('term', '').strip() | |
| definition = item.get('definition', '').strip() | |
| # Очищаем определение от лишних пробелов и переносов | |
| definition = ' '.join(definition.split()) | |
| # Экранируем кавычки в содержимом (двойные кавычки заменяем на "") | |
| term_escaped = term.replace('"', '""') | |
| definition_escaped = definition.replace('"', '""') | |
| # Записываем в формате: "Front"[TAB]"Back" | |
| file.write(f'"{term_escaped}"{delimiter}"{definition_escaped}"\n') | |
| print(f"✅ Создан текстовый файл с двумя полями: {output_file}") | |
| print(f" Карточек: {len(data)}") | |
| print(f" Формат: двухполевые карточки") | |
| except Exception as e: | |
| print(f"Ошибка при создании текстового файла: {e}") | |
| def print_preview(data: List[Dict[str, Any]], count: int = 3): | |
| """Показывает preview первых карточек""" | |
| print(f"\n📋 Превью первых {min(count, len(data))} карточек:") | |
| print("-" * 50) | |
| for i, item in enumerate(data[:count]): | |
| term = item.get('term', 'N/A') | |
| definition = item.get('definition', 'N/A') | |
| definition_short = (definition[:100] + '...') if len(definition) > 100 else definition | |
| print(f"{i+1}. {term}") | |
| print(f" {definition_short}") | |
| print() | |
| def main(): | |
| parser = argparse.ArgumentParser(description='Создание TXT файлов карточек для Anki') | |
| parser.add_argument('input_files', nargs='+', help='Путь(и) к JSON файлам (можно указать несколько файлов)') | |
| parser.add_argument('-o', '--output', default='anki_cards.txt', help='Базовое имя выходного файла') | |
| args = parser.parse_args() | |
| # Загружаем данные из всех файлов | |
| print(f"🔍 Обрабатываем файлы: {', '.join(args.input_files)}") | |
| all_data = [] | |
| processed_files = [] | |
| for file_path in sorted(args.input_files): | |
| if os.path.exists(file_path): | |
| print(f"📂 Загружаем: {file_path}") | |
| data = load_json_data(file_path) | |
| if data: | |
| all_data.extend(data) | |
| processed_files.append(file_path) | |
| print(f" ✅ Загружено {len(data)} карточек") | |
| else: | |
| print(f" ❌ Пустой файл или ошибка загрузки") | |
| else: | |
| print(f"⚠️ Файл не найден: {file_path}") | |
| if not all_data: | |
| print("❌ Нет данных для обработки") | |
| return | |
| print(f"\n📊 Итого:") | |
| print(f" Обработано файлов: {len(processed_files)}") | |
| print(f" Загружено карточек: {len(all_data)}") | |
| if processed_files: | |
| print(f" Файлы:") | |
| for file_path in processed_files: | |
| print(f" • {file_path}") | |
| # Показываем превью | |
| print_preview(all_data) | |
| # Создаем файл | |
| print(f"\n🔄 Создаем объединенный TXT файл...") | |
| create_txt_file(all_data, args.output) | |
| print(f"\n🎉 Готово! Файл можно импортировать в Anki.") | |
| print(f"\n💡 Инструкция по импорту:") | |
| print(f" 1. Откройте Anki") | |
| print(f" 2. Файл → Импорт") | |
| print(f" 3. Выберите файл: {args.output}") | |
| print(f" 4. Настройки импорта определятся автоматически") | |
| print(f" 5. Нажмите 'Импорт'") | |
| if __name__ == "__main__": | |
| # Если запускается без аргументов, используем примеры | |
| import sys | |
| if len(sys.argv) == 1: | |
| # Тестовый режим | |
| print("🧪 Тестовый режим с примером данных...") | |
| # Создаем тестовые данные на основе предоставленного примера | |
| test_data = [ | |
| { | |
| "definition": "without any delay; immediately (без лишних слов)", | |
| "term": "without further ado" | |
| }, | |
| { | |
| "definition": "(adj.) unusual and strange, sometimes in an unpleasant way (Своеобразный)", | |
| "term": "peculiar" | |
| }, | |
| { | |
| "definition": "(adj.) not willing to do something and therefore slow to do it", | |
| "term": "reluctant" | |
| } | |
| ] | |
| print_preview(test_data) | |
| print("\n📝 Создаем примеры файлов:") | |
| create_txt_file(test_data, "example_cards.txt") | |
| print(f"\n💡 Примеры использования:") | |
| print(f" # Один файл") | |
| print(f' python3 json_to_anki_txt.py "input.json"') | |
| print(f" ") | |
| print(f" # Несколько файлов") | |
| print(f' python3 json_to_anki_txt.py "file1.json" "file2.json" "file3.json"') | |
| print(f" ") | |
| print(f" # Файлы с пробелами в названии (bash развернет *)") | |
| print(f' python3 json_to_anki_txt.py From\\ The\\ Boyz.\\ Part\\ *.json') | |
| print(f" ") | |
| print(f" # Все JSON файлы в папке") | |
| print(f" python3 json_to_anki_txt.py *.json") | |
| print(f" ") | |
| print(f" # Пользовательское имя выходного файла") | |
| print(f" python3 json_to_anki_txt.py *.json -o combined_vocabulary") | |
| else: | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment