Files
hotels/create_dashboard_excel.py

277 lines
11 KiB
Python
Raw Normal View History

#!/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()