#!/usr/bin/env python3 """ ФИНАЛЬНЫЙ Excel генератор с правильной обработкой критериев """ import psycopg2 import json import openpyxl from openpyxl.styles import Font, PatternFill, Border, Side, Alignment from openpyxl.utils import get_column_letter from datetime import datetime from urllib.parse import unquote # Конфигурация БД DB_CONFIG = { 'host': '147.45.189.234', 'port': 5432, 'database': 'default_db', 'user': 'gen_user', 'password': unquote('2~~9_%5EkVsU%3F2%5CS') } def get_audit_results_from_db(): """Получить результаты аудита из БД""" try: conn = psycopg2.connect(**DB_CONFIG) cursor = conn.cursor() cursor.execute(""" SELECT ar.hotel_id, hm.full_name, hm.website_address, hm.rkn_registry_status, hm.rkn_registry_number, hm.rkn_registry_date, ar.score_percentage, ar.criteria_results, hm.created_at FROM hotel_audit_results ar JOIN hotel_main hm ON ar.hotel_id = hm.id WHERE hm.region_name = 'Чукотский автономный округ' ORDER BY hm.created_at DESC """) results = [] for row in cursor.fetchall(): result = { 'hotel_id': row[0], 'full_name': row[1], 'website_address': row[2], 'rkn_registry_status': row[3], 'rkn_registry_number': row[4], 'rkn_registry_date': row[5], 'score_percentage': row[6], 'criteria_results': row[7], 'created_at': row[8] } results.append(result) cursor.close() conn.close() return results except Exception as e: print(f"❌ Ошибка получения данных: {e}") return [] def create_excel_report(results): """Создать Excel отчёт""" wb = openpyxl.Workbook() ws = wb.active ws.title = "Аудит отелей" # Стили header_font = Font(size=12, bold=True, color="FFFFFF") header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid") 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') ) # Базовые заголовки base_headers = [ 'ID отеля', 'Название отеля', 'Сайт', 'Балл (%)', 'Дата аудита' ] # Добавляем базовые заголовки for i, header in enumerate(base_headers, 1): cell = ws.cell(row=1, column=i, value=header) cell.fill = header_fill cell.font = header_font cell.border = border ws.column_dimensions[get_column_letter(i)].width = 20 col = len(base_headers) + 1 # Заголовки критериев (БЕЗ критерия 6 - РКН) if results and results[0]['criteria_results']: criteria_results = results[0]['criteria_results'] if isinstance(criteria_results, dict): # Сортируем критерии по ключам sorted_criteria = sorted(criteria_results.items()) for criterion_key, criterion_data in sorted_criteria: criterion_num = criterion_key.split('_')[1] # извлекаем номер criterion_id = int(criterion_num) # Пропускаем критерий 6 (РКН) - он будет отдельно if criterion_id == 6: continue criterion_name = f"{criterion_id}. {criterion_data.get('name', f'Критерий {criterion_id}')}" # 3 колонки на критерий: Статус, URL, Комментарий headers = [f"{criterion_name} Статус", f"{criterion_name} URL", f"{criterion_name} Комментарий"] for header in headers: cell = ws.cell(row=1, column=col, value=header) cell.fill = header_fill cell.font = header_font cell.border = border ws.column_dimensions[get_column_letter(col)].width = 30 col += 1 # Добавляем РКН колонки после критерия 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.border = border ws.column_dimensions[get_column_letter(col)].width = 30 col += 1 print(f"✅ Заголовки созданы, всего колонок: {col-1}") # Добавляем данные for row_idx, result in enumerate(results, 2): col = 1 # Базовые данные ws.cell(row=row_idx, column=col, value=result['hotel_id']).border = border col += 1 ws.cell(row=row_idx, column=col, value=result['full_name']).border = border col += 1 ws.cell(row=row_idx, column=col, value=result['website_address'] or '-').border = border col += 1 ws.cell(row=row_idx, column=col, value=result['score_percentage']).border = border col += 1 ws.cell(row=row_idx, column=col, value=str(result['created_at'])[:10]).border = border col += 1 # Данные критериев criteria_results = result['criteria_results'] if isinstance(criteria_results, dict): # Сортируем критерии по ключам sorted_criteria = sorted(criteria_results.items()) for criterion_key, criterion_data in sorted_criteria: criterion_num = criterion_key.split('_')[1] criterion_id = int(criterion_num) # Пропускаем критерий 6 (РКН) - он будет отдельно if criterion_id == 6: continue # Статус verdict = criterion_data.get('verdict', 'НЕТ') status_cell = ws.cell(row=row_idx, column=col, value=verdict) status_cell.border = border status_cell.alignment = Alignment(horizontal='center', vertical='center') if verdict == 'ДА': status_cell.fill = found_fill else: status_cell.fill = not_found_fill col += 1 # URL urls = criterion_data.get('approval_urls', []) url = urls[0] if urls else '-' ws.cell(row=row_idx, column=col, value=url).border = border col += 1 # Комментарий quote = criterion_data.get('quote', '') or '' comment = quote[:200] + '...' if len(quote) > 200 else quote if quote else "Не найдено" ws.cell(row=row_idx, column=col, value=comment).border = border col += 1 # РКН данные из hotel_main rkn_status = result.get('rkn_registry_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 = result.get('rkn_registry_number', '') rkn_date = result.get('rkn_registry_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 # Высота строки ws.row_dimensions[row_idx].height = 50 print(f"✅ Данные добавлены, всего строк: {len(results)}") # Сохраняем файл timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"audit_final_{timestamp}.xlsx" wb.save(filename) return filename def main(): """Основная функция""" print("🚀 ГЕНЕРАЦИЯ ФИНАЛЬНОГО EXCEL") print("=" * 40) try: results = get_audit_results_from_db() if not results: print("❌ Нет данных для отчёта") return print(f"✅ Получено результатов: {len(results)}") filename = create_excel_report(results) print(f"✅ Excel отчёт сохранён: {filename}") print(f"📊 Обработано отелей: {len(results)}") if results: avg_score = sum(r['score_percentage'] for r in results) / len(results) print(f"📈 Средний % соответствия: {avg_score:.1f}%") except Exception as e: print(f"❌ Ошибка: {e}") import traceback traceback.print_exc() if __name__ == "__main__": main()