import os import json import time from datetime import datetime from urllib.request import urlopen from dotenv import load_dotenv import mysql.connector from mysql.connector import Error from ensure_db import ensure_database_structure # Загрузка переменных окружения load_dotenv() VOLK_CATEGORY_LIST = os.getenv('VOLK_CATEGORY_LIST') VOLK_API_VERSION = os.getenv('VOLK_API_VERSION') MDB_HOST = os.getenv('MDB_HOST') MDB_USER = os.getenv('MDB_USER') MDB_PW = os.getenv('MDB_PW') MDBASE = os.getenv('MDBASE') LOG_FILE = os.getenv('LOG_FILE') def log_message(message, category="VOLK_loader"): """Функция для записи в лог-файл с повторными попытками при блокировке""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") log_entry = f"[{timestamp}] [{category}] {message}\n" # До 5 попыток записи с увеличением задержки for attempt in range(5): try: with open(LOG_FILE, 'a', encoding='utf-8') as f: f.write(log_entry) return True except (IOError, OSError) as e: if attempt < 4: # Не последняя попытка time.sleep(0.4) # Задержка 400ms между попытками else: # После 5 неудачных попыток выводим в stdout print(f"Не удалось записать в лог после 5 попыток: {log_entry.strip()}") return False return False def load_categories(): """Экспортируемая функция для загрузки категорий из JSON в базу данных""" inserted_count = 0 updated_count = 0 try: log_message("Начало работы") # Проверка наличия URL if not VOLK_CATEGORY_LIST: error_msg = "Переменная окружения VOLK_CATEGORY_LIST не установлена" log_message(error_msg, "ERROR") print(error_msg) return # Чтение JSON из URL log_message(f"Чтение данных из {VOLK_CATEGORY_LIST}") with urlopen(VOLK_CATEGORY_LIST) as response: data = json.loads(response.read().decode()) # Проверка версии API api_version = data.get('api_version') if api_version != VOLK_API_VERSION: error_msg = f"Несовпадение версии API: получена версия '{api_version}', ожидается '{VOLK_API_VERSION}'. Обработка остановлена." log_message(error_msg, "ERROR") print(error_msg) return log_message(f"Версия API соответствует: {api_version}") # Проверка наличия блока data if 'data' not in data: error_msg = "В JSON отсутствует блок 'data'" log_message(error_msg, "ERROR") print(error_msg) return categories_data = data['data'] log_message(f"Получено {len(categories_data)} записей для обработки") # Подключение к базе данных connection = mysql.connector.connect( host=MDB_HOST, user=MDB_USER, password=MDB_PW, database=MDBASE ) # Установка временной зоны соединения в UTC cursor_temp = connection.cursor() cursor_temp.execute("SET time_zone = '+00:00'") cursor_temp.close() cursor = connection.cursor(dictionary=True) # Проверка структуры БД log_message("Проверка структуры базы данных") ensure_database_structure() for item in categories_data: # Проверка обязательного поля ID if 'id' not in item or not item['id']: log_message(f"Пропущена запись без id или с пустым id: {item}", "WARNING") continue # Подготовка значений category_id = item['id'] # TITLE может отсутствовать или быть пустым - в этом случае используем пустую строку title = item.get('title', '') or '' # Проверка существующей записи cursor.execute( "SELECT AUTO_ID, ID, TITLE FROM categories WHERE ID = %s", (category_id,) ) existing = cursor.fetchone() if not existing: # Вставка новой записи cursor.execute( "INSERT INTO categories (ID, TITLE) VALUES (%s, %s)", (category_id, title) ) inserted_count += 1 log_message(f"Добавление: Категория {category_id}, Название: {title if title else '(пусто)'}") else: # Обновление существующей записи (всегда обновляем TITLE, даже если оно пустое) if existing['TITLE'] != title: cursor.execute( "UPDATE categories SET TITLE = %s WHERE ID = %s", (title, category_id) ) updated_count += 1 log_message(f"Обновление: Категория {category_id}, Новое название: {title if title else '(пусто)'}") else: log_message(f"Категория {category_id} без изменений") connection.commit() log_message(f"Завершение работы. Добавлено записей: {inserted_count}. Обновлено записей: {updated_count}.") except Error as e: error_msg = f"Ошибка базы данных: {e}" log_message(error_msg, "ERROR") print(error_msg) if 'connection' in locals() and connection.is_connected(): connection.rollback() except Exception as e: error_msg = f"Общая ошибка: {e}" log_message(error_msg, "ERROR") print(error_msg) finally: if 'connection' in locals() and connection.is_connected(): cursor.close() connection.close() if __name__ == "__main__": load_categories()