185 lines
8.9 KiB
Python
185 lines
8.9 KiB
Python
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
|
||
|
||
# Используем update_or_create для обновления существующей записи
|
||
obj, created = Client.objects.update_or_create(
|
||
club_card_number=club_card_number,
|
||
defaults={
|
||
'name': item.get("name"),
|
||
'telegram_id': item.get("telegram_id"),
|
||
}
|
||
)
|
||
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
|