MacOS_Parsers/Pars_Decathlon/send_all_json_to_localAPI_only.py
va1is bd765f8349 Метод добавления товаров из xlsx с UI
+ ZH - категории теперь лежат в рекордсфолдер
2025-10-27 11:47:32 +03:00

176 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Работает не только в корне, но и в records_folder — если ты запускаешь его из папки парсера, он сам найдёт нужную директорию.
Пропускает пустые JSON-файлы (у которых items == []).
Запоминает успешно отправленные файлы — чтобы не отправлять их повторно (создаёт sent_files.txt).
Добавляет цветной вывод в консоль (удобно при большом объёме).
Улучшенный лог с временем выполнения и статусами."""
import os
import json
import time
import re
import requests
import openpyxl
from datetime import datetime
API_URL = "http://172.25.4.101:3005/parser/data"
LOG_FILE = "send_log.txt"
SENT_TRACK_FILE = "sent_files.txt"
class Colors:
OK = "\033[92m"
WARN = "\033[93m"
ERR = "\033[91m"
END = "\033[0m"
def log(msg: str, color=""):
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
line = f"[{ts}] {msg}"
print(color + line + Colors.END)
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(line + "\n")
def clean_description(text: str) -> str:
"""Очищает описание: заменяет переносы и удаляет HTML-теги, кроме <br>"""
if not text:
return ""
text = re.sub(r"(\n\s*){1,}", "<br>", text)
text = re.sub(r"<br\s*/?>\s*\n", "<br>", text, flags=re.IGNORECASE)
text = re.sub(r"\n\s*<br\s*/?>", "<br>", text, flags=re.IGNORECASE)
text = re.sub(r"<br\s*/?>\s*<br\s*/?>", "<br>", text, flags=re.IGNORECASE)
text = re.sub(r"<(?!br\s*/?)[^>]+>", "", text) # удалить всё, кроме <br>
return text.strip()
def get_brand_map(xlsx_path: str) -> dict:
"""Создаёт словарь {Артикул: Параметр: Бренд} из XLSX"""
brand_map = {}
try:
wb = openpyxl.load_workbook(xlsx_path)
ws = wb.active
# найдём номера нужных колонок
headers = [cell.value for cell in ws[1]]
if not headers:
return {}
try:
art_idx = headers.index("Артикул") + 1
brand_idx = headers.index("Параметр: Бренд") + 1
except ValueError:
log(f"⚠️ В {xlsx_path} не найдены нужные колонки.", Colors.WARN)
return {}
for row in ws.iter_rows(min_row=2, values_only=True):
sku = row[art_idx - 1]
brand = row[brand_idx - 1]
if sku and brand:
brand_map[str(sku).strip()] = str(brand).strip()
except Exception as e:
log(f"❌ Ошибка при чтении {xlsx_path}: {e}", Colors.ERR)
return brand_map
def enhance_json_with_brands(file_path: str, brand_map: dict) -> str:
"""Обновляет JSON: добавляет бренды и чистит описания"""
try:
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
except Exception as e:
log(f"❌ Ошибка чтения JSON {file_path}: {e}", Colors.ERR)
return ""
for item in data.get("items", []):
variant = item.get("variant", {})
sku = str(variant.get("sku", "")).strip()
if sku in brand_map:
item["brand"] = {"name": brand_map[sku]}
else:
# если бренд не найден, оставляем пустым
item["brand"] = {"name": ""}
# чистим description
if "originalDescription" in variant:
variant["originalDescription"] = clean_description(variant["originalDescription"])
# формируем новое имя
now_str = datetime.now().strftime("%Y%m%d_%H%M")
new_name = os.path.splitext(file_path)[0] + f"_{now_str}.json"
with open(new_name, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
log(f"🧩 Обновлён JSON → {new_name}", Colors.OK)
return new_name
def send_json_file(file_path: str):
"""Отправляет JSON на API"""
try:
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
except Exception as e:
log(f"❌ Ошибка чтения файла {file_path}: {e}", Colors.ERR)
return False
total_items = len(data.get("items", []))
if total_items == 0:
log(f"⚠️ Файл {file_path} пуст — пропуск.", Colors.WARN)
return True
log(f"📤 Отправка: {file_path} | items: {total_items}")
for attempt in range(1, 4):
try:
resp = requests.post(API_URL, json=data, timeout=30)
if resp.status_code == 200:
log(f"✅ Успешно отправлен ({attempt}-я попытка): {file_path}", Colors.OK)
return True
else:
log(f"⚠️ Ошибка {resp.status_code}: {resp.text[:200]}", Colors.WARN)
except Exception as e:
log(f"❌ Ошибка сети (попытка {attempt}): {e}", Colors.ERR)
time.sleep(5)
log(f"🚫 Не удалось отправить {file_path} после 3 попыток.", Colors.ERR)
return False
def main():
cwd = os.path.abspath(".")
records_folder = os.path.join(cwd, "records_folder")
search_dir = records_folder if os.path.isdir(records_folder) else cwd
json_files = [
os.path.join(search_dir, f)
for f in os.listdir(search_dir)
if f.lower().endswith(".json")
]
if not json_files:
log("⚠️ В папке нет JSON-файлов.", Colors.WARN)
return
log(f"🔍 Найдено {len(json_files)} JSON-файлов. Начинаем обработку...\n")
for json_file in json_files:
file_path = os.path.join(cwd, json_file)
xlsx_path = os.path.splitext(file_path)[0] + ".xlsx"
if not os.path.exists(xlsx_path):
log(f"⚠️ Нет XLSX для {json_file} → пропуск добавления брендов.", Colors.WARN)
brand_map = {}
else:
brand_map = get_brand_map(xlsx_path)
new_json = enhance_json_with_brands(file_path, brand_map)
if new_json:
send_json_file(new_json)
time.sleep(2)
log("\n🏁 Отправка завершена.", Colors.OK)
if __name__ == "__main__":
main()