176 lines
6.8 KiB
Python
176 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
from uuid import uuid4
|
||
"""
|
||
import_beerds_sql.py
|
||
|
||
Скрипт читает файлы из каталогов:
|
||
- server/modules/descriptions/repository/breed_descriptions
|
||
- server/modules/descriptions/repository/breed_signs
|
||
|
||
и вместо непосредственной вставки генерирует PostgreSQL‑SQL‑файл, который
|
||
можно применить через `psql -f <имя_файла.sql>`:
|
||
|
||
CREATE TABLE beerds.beerds (
|
||
id varchar NOT NULL,
|
||
name text NOT NULL,
|
||
descriptions text NOT NULL,
|
||
signs json NOT NULL,
|
||
alias text NOT NULL,
|
||
CONSTRAINT beerds_pkey PRIMARY KEY (id)
|
||
);
|
||
"""
|
||
|
||
import sys
|
||
import json
|
||
import hashlib
|
||
import pathlib
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# 2️⃣ Путь к каталогам (можно изменить под свой проект)
|
||
# ------------------------------------------------------------------
|
||
BASE_DIR = pathlib.Path(__file__).resolve().parent.parent
|
||
DESC_DIR = BASE_DIR / "server/modules/descriptions/repository/breed_descriptions"
|
||
SIGN_DIR = BASE_DIR / "server/modules/descriptions/repository/breed_signs"
|
||
print(f"DESC_DIR = {DESC_DIR}")
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# 3️⃣ Функции чтения файлов
|
||
# ------------------------------------------------------------------
|
||
def read_text_file(path: pathlib.Path) -> str:
|
||
"""Возвращает содержимое текстового файла (unicode)."""
|
||
with path.open("r", encoding="utf-8") as f:
|
||
return f.read().strip()
|
||
|
||
|
||
def read_json_file(path: pathlib.Path) -> dict:
|
||
"""Возвращает JSON‑объект из файла."""
|
||
with path.open("r", encoding="utf-8") as f:
|
||
return json.load(f)
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# 4️⃣ Формируем словарь: имя → (описание, сигналы)
|
||
# ------------------------------------------------------------------
|
||
def build_breed_map(desc_dir: pathlib.Path, sign_dir: pathlib.Path) -> dict:
|
||
"""Возвращает dict: stem → (description, signs)"""
|
||
desc_files = {p.stem: p for p in desc_dir.glob("*.txt")}
|
||
sign_files = {p.stem: p for p in sign_dir.glob("*.txt")}
|
||
for key in desc_files:
|
||
if key in sign_files:
|
||
continue
|
||
sign_files[key] = None
|
||
|
||
|
||
common_keys = desc_files.keys() & sign_files.keys()
|
||
if not common_keys:
|
||
print("⚠️ Нет общих файлов между каталогами описаний и сигналов.")
|
||
return {}
|
||
|
||
breeds = {}
|
||
for key in sorted(common_keys):
|
||
desc_path = desc_files[key]
|
||
sign_path = sign_files[key]
|
||
try:
|
||
description = read_text_file(desc_path)
|
||
except Exception as e:
|
||
print(f"❌ Не удалось прочитать описание {desc_path}: {e}")
|
||
continue
|
||
try:
|
||
if sign_path is not None:
|
||
signs = read_json_file(sign_path)
|
||
else:
|
||
signs = {}
|
||
except Exception as e:
|
||
print(f"❌ Не удалось прочитать сигналы {sign_path}: {e}")
|
||
continue
|
||
breeds[key] = (description, signs)
|
||
return breeds
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# 5️⃣ Генерация id (если нужно уникальное)
|
||
# ------------------------------------------------------------------
|
||
def generate_id(name: str, alias: str) -> str:
|
||
"""Генерирует MD5‑hash из `name:alias`."""
|
||
key = f"{name}:{alias}"
|
||
return hashlib.md5(key.encode("utf-8")).hexdigest()
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# 6️⃣ Генерация SQL‑файла
|
||
# ------------------------------------------------------------------
|
||
def write_sql_file(breeds: dict, out_path: pathlib.Path):
|
||
"""Создаёт один .sql‑файл, содержащий INSERT‑операции для всех пород."""
|
||
if not breeds:
|
||
print("⚠️ Нет данных для генерации SQL.")
|
||
return
|
||
|
||
lines = []
|
||
header = (
|
||
"-- Автоматически сгенерированный скрипт вставки пород\n"
|
||
"-- Запустить: psql -f beerds_insert.sql\n\n"
|
||
"BEGIN;\n"
|
||
)
|
||
lines.append(header)
|
||
|
||
# Формируем один блок INSERT с несколькими строками VALUES
|
||
# 1. Столбцы
|
||
cols = "(id, name, descriptions, signs, alias)"
|
||
# 2. Генерируем строки VALUES
|
||
value_lines = []
|
||
for name, (description, signs) in breeds.items():
|
||
alias = name # можно изменить при необходимости
|
||
breed_id = generate_id(name, alias)
|
||
# Подготовка значений: экранирование апострофов и JSON
|
||
escaped_name = name.replace("_", " ").replace("'", "''")
|
||
escaped_alias = alias.replace("'", "''")
|
||
escaped_desc = description.replace("'", "''")
|
||
# JSON как строка (в PostgreSQL json можно передавать как текст)
|
||
json_str = json.dumps(signs, ensure_ascii=False).replace("'", "''")
|
||
value_line = f"('{breed_id}', '{escaped_name}', '{escaped_desc}', '{json_str}', '{escaped_alias}')"
|
||
value_lines.append(value_line)
|
||
|
||
# Объединяем VALUES через запятую
|
||
values_section = ",\n".join(value_lines)
|
||
insert_stmt = (
|
||
f"INSERT INTO beerds.beerds {cols}\n"
|
||
f"VALUES\n{values_section}\n"
|
||
f"ON CONFLICT (id) DO UPDATE\n"
|
||
f" SET name = EXCLUDED.name,\n"
|
||
f" descriptions = EXCLUDED.descriptions,\n"
|
||
f" signs = EXCLUDED.signs,\n"
|
||
f" alias = EXCLUDED.alias;\n"
|
||
)
|
||
lines.append(insert_stmt)
|
||
lines.append("\nCOMMIT;")
|
||
|
||
# Записываем в файл
|
||
out_path.write_text("\n".join(lines), encoding="utf-8")
|
||
print(f"✅ SQL‑файл успешно сгенерирован: {out_path}")
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# 7️⃣ Основная точка входа
|
||
# ------------------------------------------------------------------
|
||
def main():
|
||
print("🔍 Читаем файлы…")
|
||
breeds_map = build_breed_map(DESC_DIR, SIGN_DIR)
|
||
|
||
if not breeds_map:
|
||
print("❌ Ничего не найдено. Завершение.")
|
||
sys.exit(1)
|
||
|
||
# Путь для вывода SQL‑файла
|
||
sql_file_path = BASE_DIR / "beerds_insert.sql"
|
||
print(f"📝 Записываем INSERT‑операции в файл: {sql_file_path}")
|
||
write_sql_file(breeds_map, sql_file_path)
|
||
|
||
print("🎉 Готово!")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|