#!/usr/bin/env python3 """ Генерация Excel из JSON файла (экспортированного из n8n) """ import json import openpyxl from openpyxl.styles import Font, PatternFill, Border, Side, Alignment from openpyxl.utils import get_column_letter from datetime import datetime def create_excel_from_json(json_file): """Создать Excel отчёт из JSON файла""" # Читаем JSON with open(json_file, 'r', encoding='utf-8') as f: results = json.load(f) print(f"📊 Найдено результатов аудита: {len(results)}") wb = openpyxl.Workbook() ws = wb.active ws.title = "Аудит" # Стили header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid") header_font = Font(color="FFFFFF", bold=True, size=10) found_fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid") not_found_fill = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid") border = Border( left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin') ) # ЗАГОЛОВКИ (строка 1) col = 1 base_headers = ['Отель', 'Сайт', 'Есть сайт', 'Балл', 'Процент'] for header in base_headers: cell = ws.cell(row=1, column=col, value=header) cell.fill = header_fill cell.font = header_font cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) cell.border = border col += 1 # Заголовки критериев (включая РКН в правильном месте) if results and results[0].get('criteria_results'): criteria_results = results[0]['criteria_results'] if isinstance(criteria_results, str): criteria_results = json.loads(criteria_results) print(f"🔍 criteria_results type: {type(criteria_results)}") # Если это список (из n8n) - используем напрямую if isinstance(criteria_results, list): criteria_list = criteria_results print(f"🔍 Найдено критериев (список): {len(criteria_list)}") # Если это словарь (из БД) - преобразуем elif isinstance(criteria_results, dict): criteria_list = [] for i in range(1, 19): # критерии 1-18 key = f'criterion_{i:02d}' if key in criteria_results: criterion_data = criteria_results[key] criteria_list.append({ 'criterion_id': i, 'criterion_name': criterion_data.get('name', f'Критерий {i}'), }) print(f"🔍 Найдено критериев (словарь): {len(criteria_list)}") else: criteria_list = [] print(f"🔍 Неизвестный тип criteria_results") for criterion_idx, criterion in enumerate(criteria_list): # Вставляем РКН заголовки после критерия 5 (индекс 5) if criterion_idx == 5: rkn_headers = ['6. РКН Реестр', '6. РКН Номер/Дата', '6. РКН Ссылка'] for header in rkn_headers: cell = ws.cell(row=1, column=col, value=header) cell.fill = header_fill cell.font = header_font cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) cell.border = border ws.column_dimensions[get_column_letter(col)].width = 30 col += 1 criterion_name = f"{criterion['criterion_id']}. {criterion['criterion_name']}" # Колонка 1: Статус (ДА/НЕТ) cell = ws.cell(row=1, column=col, value=criterion_name) cell.fill = header_fill cell.font = header_font cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) cell.border = border ws.column_dimensions[get_column_letter(col)].width = 35 col += 1 # Колонка 2: URL cell = ws.cell(row=1, column=col, value=f"{criterion['criterion_id']}. Апрув URL") cell.fill = header_fill cell.font = header_font cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) cell.border = border ws.column_dimensions[get_column_letter(col)].width = 40 col += 1 # Колонка 3: Цитата/Детали cell = ws.cell(row=1, column=col, value=f"{criterion['criterion_id']}. Комментарий") cell.fill = header_fill cell.font = header_font cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) cell.border = border ws.column_dimensions[get_column_letter(col)].width = 50 col += 1 # Высота строки заголовков ws.row_dimensions[1].height = 40 print(f"✅ Заголовки созданы, всего колонок: {col-1}") # ДАННЫЕ (строки 2+) for row_idx, result in enumerate(results, 2): col = 1 # Базовые данные cell = ws.cell(row=row_idx, column=col, value=result['hotel_name']) cell.border = border cell.alignment = Alignment(vertical='top', wrap_text=True) col += 1 cell = ws.cell(row=row_idx, column=col, value=result.get('website', 'НЕТ САЙТА')) cell.border = border cell.alignment = Alignment(vertical='top') col += 1 has_website = "Да" if result.get('has_website') else "Нет" cell = ws.cell(row=row_idx, column=col, value=has_website) cell.border = border cell.alignment = Alignment(horizontal='center', vertical='center') col += 1 cell = ws.cell(row=row_idx, column=col, value=result['total_score']) cell.border = border cell.alignment = Alignment(horizontal='center', vertical='center') col += 1 perc_cell = ws.cell(row=row_idx, column=col, value=f"{result['score_percentage']:.1f}%") perc_cell.border = border perc_cell.alignment = Alignment(horizontal='center', vertical='center') if result['score_percentage'] >= 70: perc_cell.fill = found_fill elif result['score_percentage'] < 50: perc_cell.fill = not_found_fill col += 1 # Данные по критериям criteria_results = result['criteria_results'] if isinstance(criteria_results, str): criteria_results = json.loads(criteria_results) # Если это список (из n8n) if isinstance(criteria_results, list): criteria_list = criteria_results # Если это словарь (из БД) elif isinstance(criteria_results, dict): criteria_list = [] for i in range(1, 19): key = f'criterion_{i:02d}' if key in criteria_results: criteria_list.append(criteria_results[key]) else: criteria_list = [] for criterion_idx, criterion in enumerate(criteria_list): # Вставляем РКН колонки после критерия 5 (индекс 5) if criterion_idx == 5: # РКН данные rkn_status = criterion.get('rkn_status', '') rkn_in_registry = "ДА" if rkn_status and rkn_status.lower() == 'found' else "НЕТ" rkn_status_cell = ws.cell(row=row_idx, column=col, value=rkn_in_registry) rkn_status_cell.border = border rkn_status_cell.alignment = Alignment(horizontal='center', vertical='center') if rkn_in_registry == "ДА": rkn_status_cell.fill = found_fill # Зелёный - хорошо если в реестре else: rkn_status_cell.fill = not_found_fill # Красный - плохо если НЕ в реестре col += 1 rkn_number = criterion.get('rkn_number', '') rkn_date = criterion.get('rkn_date', '') rkn_info_text = f"{rkn_number}\n{rkn_date}" if rkn_number or rkn_date else "-" cell = ws.cell(row=row_idx, column=col, value=rkn_info_text) cell.border = border cell.alignment = Alignment(vertical='top', wrap_text=True) col += 1 rkn_url = f"https://rkn.gov.ru/mass-communications/reestr/search/?q={rkn_number}" if rkn_number else "-" cell = ws.cell(row=row_idx, column=col, value=rkn_url) cell.border = border cell.alignment = Alignment(vertical='top') col += 1 # Колонка 1: Статус (ДА/НЕТ) status = "ДА" if criterion.get('found') else "НЕТ" status_cell = ws.cell(row=row_idx, column=col, value=status) status_cell.border = border status_cell.alignment = Alignment(horizontal='center', vertical='center') if criterion.get('found'): status_cell.fill = found_fill else: status_cell.fill = not_found_fill col += 1 # Колонка 2: URL url = '-' if criterion.get('ai_agent', {}).get('url'): url = criterion['ai_agent']['url'] cell = ws.cell(row=row_idx, column=col, value=url) cell.border = border cell.alignment = Alignment(vertical='top') col += 1 # Колонка 3: Комментарий/Цитата comment = "Не найдено" if criterion.get('found'): # Приоритет: ai_agent.details → ai_agent.quote → regex.extracted if criterion.get('ai_agent', {}).get('details'): comment = criterion['ai_agent']['details'] elif criterion.get('ai_agent', {}).get('quote'): comment = criterion['ai_agent']['quote'] elif criterion.get('regex', {}).get('extracted'): comment = f"[Regex] {criterion['regex']['extracted']}" else: comment = "Найдено" # Ограничиваем длину comment = comment[:200] + "..." if len(comment) > 200 else comment cell = ws.cell(row=row_idx, column=col, value=comment) cell.border = border cell.alignment = Alignment(vertical='top', wrap_text=True) col += 1 # Высота строки ws.row_dimensions[row_idx].height = 50 print(f"✅ Данные добавлены, всего строк: {len(results)}") # Сохраняем файл timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"audit_from_json_{timestamp}.xlsx" wb.save(filename) return filename def main(): """Основная функция""" print("🚀 ГЕНЕРАЦИЯ EXCEL ИЗ JSON") print("=" * 50) json_file = "audit_data.json" print(f"📂 Читаю файл: {json_file}") try: filename = create_excel_from_json(json_file) print(f"✅ Excel отчёт сохранён: {filename}") except FileNotFoundError: print(f"❌ Файл {json_file} не найден") print(f"📝 Создайте файл {json_file} с данными из n8n") except Exception as e: print(f"❌ Ошибка: {e}") if __name__ == "__main__": main()