#!/usr/bin/env python3 """ Создание Excel дашборда с аудитом отелей Лист 1: Дашборд с графиками и статистикой Лист 2: Детальная таблица аудита по отелям """ import psycopg2 from psycopg2.extras import RealDictCursor from urllib.parse import unquote import pandas as pd import openpyxl from openpyxl import Workbook from openpyxl.styles import Font, Alignment, PatternFill, Border, Side, NamedStyle from openpyxl.chart import BarChart, PieChart, LineChart, Reference from openpyxl.chart.label import DataLabelList from openpyxl.utils.dataframe import dataframe_to_rows from openpyxl.drawing.image import Image from datetime import datetime import json 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_data(): """Получить данные аудита из БД""" conn = psycopg2.connect(**DB_CONFIG, cursor_factory=RealDictCursor) cur = conn.cursor() # Получаем данные аудита с информацией об отелях cur.execute(""" SELECT har.hotel_id, har.hotel_name, har.region_name, har.website, har.has_website, har.total_score, har.max_score, har.score_percentage, har.audit_date, har.audit_version, har.criteria_results, hm.full_name, hm.website_address, hm.owner_inn, hm.owner_ogrn, hm.addresses, hm.phone, hm.email FROM hotel_audit_results har LEFT JOIN hotel_main hm ON hm.id = har.hotel_id ORDER BY har.audit_date DESC """) audit_data = cur.fetchall() # Статистика по регионам cur.execute(""" SELECT region_name, COUNT(*) as total_hotels, AVG(score_percentage) as avg_score, MAX(audit_date) as last_audit, COUNT(CASE WHEN has_website = true THEN 1 END) as with_website, COUNT(CASE WHEN score_percentage >= 50 THEN 1 END) as compliant_hotels FROM hotel_audit_results WHERE region_name IS NOT NULL GROUP BY region_name ORDER BY total_hotels DESC """) region_stats = cur.fetchall() # Статистика по критериям (пропускаем пока - сложная структура JSONB) criteria_stats = [] cur.close() conn.close() return audit_data, region_stats, criteria_stats def create_dashboard_sheet(workbook, region_stats, criteria_stats): """Создать лист дашборда""" ws = workbook.active ws.title = "📊 Дашборд" # Стили header_font = Font(name='Arial', size=14, bold=True, color='FFFFFF') header_fill = PatternFill(start_color='366092', end_color='366092', fill_type='solid') subheader_font = Font(name='Arial', size=12, bold=True) normal_font = Font(name='Arial', size=10) # Заголовок ws['A1'] = "🏨 ДАШБОРД АУДИТА ОТЕЛЕЙ" ws['A1'].font = Font(name='Arial', size=16, bold=True, color='366092') ws['A1'].alignment = Alignment(horizontal='center') ws.merge_cells('A1:H1') # Общая статистика ws['A3'] = "📈 ОБЩАЯ СТАТИСТИКА" ws['A3'].font = subheader_font ws['A3'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid') # Подсчитываем общую статистику total_hotels = sum(row['total_hotels'] for row in region_stats) total_with_website = sum(row['with_website'] for row in region_stats) total_compliant = sum(row['compliant_hotels'] for row in region_stats) avg_score = sum(row['avg_score'] * row['total_hotels'] for row in region_stats) / total_hotels if total_hotels > 0 else 0 ws['A4'] = f"Всего отелей проаудировано: {total_hotels}" ws['A5'] = f"Отелей с сайтами: {total_with_website} ({total_with_website/total_hotels*100:.1f}%)" ws['A6'] = f"Соответствующих требованиям (≥50%): {total_compliant} ({total_compliant/total_hotels*100:.1f}%)" ws['A7'] = f"Средний балл соответствия: {avg_score:.1f}%" for cell in ['A4', 'A5', 'A6', 'A7']: ws[cell].font = normal_font # Статистика по регионам ws['A9'] = "📍 СТАТИСТИКА ПО РЕГИОНАМ" ws['A9'].font = subheader_font ws['A9'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid') # Заголовки таблицы регионов headers = ['Регион', 'Отелей', 'С сайтами', 'Соответствуют', 'Средний балл', 'Последний аудит'] for i, header in enumerate(headers, 1): cell = ws.cell(row=10, column=i, value=header) cell.font = header_font cell.fill = header_fill cell.alignment = Alignment(horizontal='center') # Данные по регионам for i, region in enumerate(region_stats, 11): ws.cell(row=i, column=1, value=region['region_name']) ws.cell(row=i, column=2, value=region['total_hotels']) ws.cell(row=i, column=3, value=region['with_website']) ws.cell(row=i, column=4, value=region['compliant_hotels']) ws.cell(row=i, column=5, value=f"{region['avg_score']:.1f}%") ws.cell(row=i, column=6, value=region['last_audit'].strftime('%d.%m.%Y') if region['last_audit'] else '') # Форматирование for col in range(1, 7): ws.cell(row=i, column=col).font = normal_font ws.cell(row=i, column=col).alignment = Alignment(horizontal='center') # График по регионам chart1 = BarChart() chart1.title = "Количество отелей по регионам" chart1.x_axis.title = "Регионы" chart1.y_axis.title = "Количество отелей" data = Reference(ws, min_col=2, min_row=10, max_row=10+len(region_stats), max_col=2) cats = Reference(ws, min_col=1, min_row=11, max_row=10+len(region_stats)) chart1.add_data(data, titles_from_data=False) chart1.set_categories(cats) chart1.height = 10 chart1.width = 15 ws.add_chart(chart1, "A15") # Информация о дате создания ws['A30'] = f"📅 Отчет создан: {datetime.now().strftime('%d.%m.%Y %H:%M')}" ws['A30'].font = Font(name='Arial', size=10, italic=True, color='666666') # Настройка ширины колонок column_widths = [25, 10, 12, 15, 12, 15] for i, width in enumerate(column_widths, 1): ws.column_dimensions[openpyxl.utils.get_column_letter(i)].width = width def create_audit_sheet(workbook, audit_data): """Создать лист детального аудита""" ws = workbook.create_sheet("🏨 Аудит отелей") # Стили header_font = Font(name='Arial', size=12, bold=True, color='FFFFFF') header_fill = PatternFill(start_color='366092', end_color='366092', fill_type='solid') normal_font = Font(name='Arial', size=9) # Заголовки headers = [ 'Отель', 'Регион', 'Сайт', 'Есть сайт', 'Балл', 'Процент', 'ИНН', 'ОГРН', 'Адрес', 'Телефон', 'Email', 'Дата аудита' ] for i, header in enumerate(headers, 1): cell = ws.cell(row=1, column=i, value=header) cell.font = header_font cell.fill = header_fill cell.alignment = Alignment(horizontal='center') # Данные for i, hotel in enumerate(audit_data, 2): ws.cell(row=i, column=1, value=hotel['hotel_name'] or hotel['full_name']) ws.cell(row=i, column=2, value=hotel['region_name']) ws.cell(row=i, column=3, value=hotel['website'] or hotel['website_address']) ws.cell(row=i, column=4, value='Да' if hotel['has_website'] else 'Нет') ws.cell(row=i, column=5, value=f"{hotel['total_score']}/{hotel['max_score']}") ws.cell(row=i, column=6, value=f"{hotel['score_percentage']:.1f}%") ws.cell(row=i, column=7, value=hotel['owner_inn']) ws.cell(row=i, column=8, value=hotel['owner_ogrn']) ws.cell(row=i, column=9, value=str(hotel['addresses']) if hotel['addresses'] else '') ws.cell(row=i, column=10, value=hotel['phone']) ws.cell(row=i, column=11, value=hotel['email']) ws.cell(row=i, column=12, value=hotel['audit_date'].strftime('%d.%m.%Y %H:%M') if hotel['audit_date'] else '') # Цветовое кодирование процента percentage = hotel['score_percentage'] or 0 if percentage >= 70: fill_color = 'C6EFCE' # Зеленый elif percentage >= 40: fill_color = 'FFEB9C' # Желтый else: fill_color = 'FFC7CE' # Красный ws.cell(row=i, column=6).fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid') # Форматирование for col in range(1, 13): ws.cell(row=i, column=col).font = normal_font ws.cell(row=i, column=col).alignment = Alignment(horizontal='center') # Настройка ширины колонок column_widths = [30, 20, 25, 10, 10, 10, 15, 15, 30, 15, 20, 15] for i, width in enumerate(column_widths, 1): ws.column_dimensions[openpyxl.utils.get_column_letter(i)].width = width # Фильтры ws.auto_filter.ref = f"A1:{openpyxl.utils.get_column_letter(len(headers))}{len(audit_data)+1}" # Заморозка заголовков ws.freeze_panes = "A2" def main(): """Основная функция""" print("🚀 СОЗДАНИЕ EXCEL ДАШБОРДА") print("=" * 50) # Получаем данные print("📊 Загружаем данные из БД...") audit_data, region_stats, criteria_stats = get_audit_data() print(f"✅ Загружено:") print(f" 🏨 Отелей: {len(audit_data)}") print(f" 📍 Регионов: {len(region_stats)}") print(f" 🎯 Критериев: {len(criteria_stats)}") # Создаем Excel файл print("\n📝 Создаем Excel файл...") workbook = Workbook() # Лист дашборда print("📊 Создаем дашборд...") create_dashboard_sheet(workbook, region_stats, criteria_stats) # Лист аудита print("🏨 Создаем таблицу аудита...") create_audit_sheet(workbook, audit_data) # Сохраняем файл timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"hotel_audit_dashboard_{timestamp}.xlsx" workbook.save(filename) print(f"\n✅ Дашборд сохранен: {filename}") print(f"📊 Листы:") print(f" 📈 Дашборд - графики и статистика") print(f" 🏨 Аудит отелей - детальная таблица") if __name__ == "__main__": main()