import json import logging from django.core.exceptions import ImproperlyConfigured from .models import APISettings, Client, Invoice import requests logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) if not logger.handlers: console_handler = logging.StreamHandler() console_handler.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') console_handler.setFormatter(formatter) logger.addHandler(console_handler) class APIClient: """ Класс для подключения к API и получения данных. Данные API_URL и API_KEY берутся из модели APISettings. """ def __init__(self): self.settings = APISettings.objects.first() if not self.settings: logger.error("Настройки API не сконфигурированы. Заполните модель APISettings.") raise ImproperlyConfigured("Настройки API не сконфигурированы.") self.api_url = self.settings.api_url.rstrip("/") self.api_key = self.settings.api_key self.headers = {"X-API-Key": self.api_key} logger.debug(f"APIClient инициализирован с базовым URL: {self.api_url}") def get_clients(self): url = f"{self.api_url}/api/clients" logger.debug(f"Запрос клиентов по адресу: {url} с заголовками: {self.headers}") try: response = requests.get(url, headers=self.headers, timeout=10) response.raise_for_status() data = response.json() logger.debug("Запрос клиентов успешно выполнен. Данные получены.") return data except requests.exceptions.RequestException as e: logger.error(f"Ошибка при запросе клиентов: {e}") return None def get_invoices(self): url = f"{self.api_url}/api/invoices" logger.debug(f"Запрос счетов по адресу: {url} с заголовками: {self.headers}") try: response = requests.get(url, headers=self.headers, timeout=10) response.raise_for_status() data = response.json() logger.debug("Запрос счетов успешно выполнен. Данные получены.") return data except requests.exceptions.RequestException as e: logger.error(f"Ошибка при запросе счетов: {e}") return None class API_SYNC: """ Класс для импорта и обновления данных из API. Методы sync_clients и sync_invoices обновляют существующие записи или создают новые. """ def __init__(self): self.logger = logger def sync_clients(self): api_client = APIClient() data = api_client.get_clients() new_or_updated = 0 if isinstance(data, dict): clients_list = data.get("member", []) self.logger.debug(f"Извлечен список клиентов из ключа 'member', найдено записей: {len(clients_list)}") elif isinstance(data, list): clients_list = data self.logger.debug(f"Получен список клиентов (list), количество записей: {len(clients_list)}") else: self.logger.error("Неожиданный формат данных клиентов от API: %s", type(data)) return new_or_updated for index, item in enumerate(clients_list, start=1): self.logger.debug(f"Обработка записи клиента {index}: {item}") if isinstance(item, str): try: item = json.loads(item) self.logger.debug(f"Запись клиента {index} успешно преобразована из строки в словарь.") except Exception as e: self.logger.error("Невозможно преобразовать запись клиента %s в словарь: %s; ошибка: %s", index, item, e) continue if isinstance(item, dict): club_card_number = item.get("club_card_num") if not club_card_number: self.logger.warning("Запись клиента %s пропущена: отсутствует club_card_num. Запись: %s", index, item) continue defaults = { 'name': item.get("name"), } telegram_id = item.get("telegram_id") if telegram_id: defaults['telegram_id'] = telegram_id # Обновим только если значение есть obj, created = Client.objects.update_or_create( club_card_number=club_card_number, defaults=defaults ) new_or_updated += 1 if created: self.logger.info("Запись клиента %s создана: club_card_num %s.", index, club_card_number) else: self.logger.info("Запись клиента %s обновлена: club_card_num %s.", index, club_card_number) else: self.logger.error("Запись клиента %s имеет неожиданный тип: %s. Значение: %s", index, type(item), item) return new_or_updated def sync_invoices(self): api_client = APIClient() data = api_client.get_invoices() new_or_updated = 0 if isinstance(data, dict): invoices_list = data.get("member", []) self.logger.debug(f"Извлечен список счетов из ключа 'member', найдено записей: {len(invoices_list)}") elif isinstance(data, list): invoices_list = data self.logger.debug(f"Получен список счетов (list), количество записей: {len(invoices_list)}") else: self.logger.error("Неожиданный формат данных счетов от API: %s", type(data)) return new_or_updated for index, invoice in enumerate(invoices_list, start=1): self.logger.debug(f"Обработка счета {index}: {invoice}") if isinstance(invoice, str): try: invoice = json.loads(invoice) self.logger.debug(f"Счет {index} успешно преобразован из строки в словарь.") except Exception as e: self.logger.error("Невозможно преобразовать счет %s в словарь: %s; ошибка: %s", index, invoice, e) continue if not isinstance(invoice, dict): self.logger.error("Счет %s имеет неожиданный тип: %s", index, type(invoice)) continue api_id = invoice.get("@id") if not api_id: self.logger.warning("Счет %s пропущен: отсутствует '@id'.", index) continue # Извлекаем данные клиента из вложенного объекта client_data = invoice.get("client", {}) client_name = client_data.get("name") client_club_card_number = client_data.get("club_card_num") # Приводим даты (при необходимости, можно использовать парсинг) created_at = invoice.get("created_at") closed_at = invoice.get("closed_at") if closed_at in [None, "N/A"]: closed_at = None defaults = { 'invoice_type': invoice.get("@type"), 'created_at': created_at, 'closed_at': closed_at, 'ext_id': invoice.get("ext_id"), 'ext_type': invoice.get("ext_type"), 'client_name': client_name, 'client_club_card_number': client_club_card_number, 'sum': invoice.get("sum"), 'company_number': invoice.get("comp_num"), 'bonus': invoice.get("bonus"), 'start_bonus': invoice.get("start_bonus"), 'deposit_sum': invoice.get("deposit_sum"), 'notes': invoice.get("notes"), } obj, created = Invoice.objects.update_or_create( api_id=api_id, defaults=defaults ) new_or_updated += 1 if created: self.logger.info("Счет %s создан: api_id=%s", index, api_id) else: self.logger.info("Счет %s обновлен: api_id=%s", index, api_id) return new_or_updated