Files
hotels/create_chukotka_report.py
Фёдор 0cf3297290 Проект аудита отелей: основные скрипты и документация
- Краулеры: smart_crawler.py, regional_crawler.py
- Аудит: audit_orel_to_excel.py, audit_chukotka_to_excel.py
- РКН проверка: check_rkn_registry.py, recheck_unclear_rkn.py
- Отчёты: create_orel_horizontal_report.py
- Обработка: process_all_hotels_embeddings.py
- Документация: README.md, DB_SCHEMA_REFERENCE.md
2025-10-16 10:52:09 +03:00

428 lines
19 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.

#!/usr/bin/env python3
"""
Создание Excel отчета по Чукотке (версия v1.0_with_rkn)
Лист 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_chukotka_data():
"""Получить данные аудита Чукотки версии v1.0_with_rkn"""
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,
hm.website_status,
hm.rkn_registry_status,
hm.rkn_registry_number,
hm.rkn_registry_date,
hm.rkn_checked_at
FROM hotel_audit_results har
LEFT JOIN hotel_main hm ON hm.id = har.hotel_id
WHERE har.region_name = 'Чукотский автономный округ'
AND har.audit_version = 'v1.0_with_rkn'
ORDER BY har.score_percentage DESC
""")
audit_data = cur.fetchall()
# Статистика по критериям (анализируем criteria_results)
criteria_stats = []
if audit_data:
# Собираем статистику по критериям из всех отелей
criteria_counts = {}
total_hotels = len(audit_data)
for hotel in audit_data:
if hotel['criteria_results']:
criteria = hotel['criteria_results']
for criterion in criteria:
name = criterion.get('criterion_name', 'Неизвестно')
found = criterion.get('found', False)
if name not in criteria_counts:
criteria_counts[name] = {'total': 0, 'found': 0}
criteria_counts[name]['total'] += 1
if found:
criteria_counts[name]['found'] += 1
# Преобразуем в список
for name, counts in criteria_counts.items():
percentage = (counts['found'] / counts['total'] * 100) if counts['total'] > 0 else 0
criteria_stats.append({
'criterion_name': name,
'total_checks': counts['total'],
'found_count': counts['found'],
'percentage': percentage
})
# Сортируем по проценту выполнения
criteria_stats.sort(key=lambda x: x['percentage'], reverse=True)
cur.close()
conn.close()
return audit_data, criteria_stats
def create_dashboard_sheet(workbook, audit_data, 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 = len(audit_data)
total_with_website = sum(1 for h in audit_data if h['has_website'])
total_compliant = sum(1 for h in audit_data if h['score_percentage'] >= 50)
avg_score = sum(h['score_percentage'] for h in audit_data) / 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}%"
ws['A8'] = f"Версия аудита: v1.0_with_rkn (с РКН проверкой)"
for cell in ['A4', 'A5', 'A6', 'A7', 'A8']:
ws[cell].font = normal_font
# Статистика по отелям
ws['A10'] = "🏨 РЕЗУЛЬТАТЫ ПО ОТЕЛЯМ"
ws['A10'].font = subheader_font
ws['A10'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
# Заголовки таблицы отелей
headers = ['Отель', 'Сайт', 'Балл', 'Процент', 'Статус', 'Дата аудита']
for i, header in enumerate(headers, 1):
cell = ws.cell(row=11, column=i, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal='center')
# Данные по отелям
for i, hotel in enumerate(audit_data, 12):
status = '✅ Соответствует' if hotel['score_percentage'] >= 50 else '⚠️ Частично' if hotel['score_percentage'] >= 30 else 'Не соответствует'
ws.cell(row=i, column=1, value=hotel['hotel_name'])
ws.cell(row=i, column=2, value=hotel['website'])
ws.cell(row=i, column=3, value=f"{hotel['total_score']}/{hotel['max_score']}")
ws.cell(row=i, column=4, value=f"{hotel['score_percentage']:.1f}%")
ws.cell(row=i, column=5, value=status)
ws.cell(row=i, column=6, value=hotel['audit_date'].strftime('%d.%m.%Y %H:%M'))
# Цветовое кодирование процента
percentage = hotel['score_percentage']
if percentage >= 50:
fill_color = 'C6EFCE' # Зеленый
elif percentage >= 30:
fill_color = 'FFEB9C' # Желтый
else:
fill_color = 'FFC7CE' # Красный
ws.cell(row=i, column=4).fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')
# Форматирование
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=4, min_row=11, max_row=11+len(audit_data), max_col=4)
cats = Reference(ws, min_col=1, min_row=12, max_row=11+len(audit_data))
chart1.add_data(data, titles_from_data=False)
chart1.set_categories(cats)
chart1.height = 10
chart1.width = 15
ws.add_chart(chart1, "A15")
# Статистика по критериям
if criteria_stats:
ws['A30'] = "🎯 ВЫПОЛНЕНИЕ КРИТЕРИЕВ"
ws['A30'].font = subheader_font
ws['A30'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
# Заголовки таблицы критериев
criteria_headers = ['Критерий', 'Проверено', 'Найдено', 'Процент выполнения']
for i, header in enumerate(criteria_headers, 1):
cell = ws.cell(row=31, column=i, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal='center')
# Данные по критериям
for i, criterion in enumerate(criteria_stats, 32):
ws.cell(row=i, column=1, value=criterion['criterion_name'])
ws.cell(row=i, column=2, value=criterion['total_checks'])
ws.cell(row=i, column=3, value=criterion['found_count'])
ws.cell(row=i, column=4, value=f"{criterion['percentage']:.1f}%")
# Цветовое кодирование процента
percentage = criterion['percentage']
if percentage >= 70:
fill_color = 'C6EFCE' # Зеленый
elif percentage >= 40:
fill_color = 'FFEB9C' # Желтый
else:
fill_color = 'FFC7CE' # Красный
ws.cell(row=i, column=4).fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')
# Форматирование
for col in range(1, 5):
ws.cell(row=i, column=col).font = normal_font
ws.cell(row=i, column=col).alignment = Alignment(horizontal='center')
# График по критериям
chart2 = BarChart()
chart2.title = "Выполнение критериев (%)"
chart2.x_axis.title = "Критерии"
chart2.y_axis.title = "Процент выполнения"
data2 = Reference(ws, min_col=4, min_row=31, max_row=31+len(criteria_stats), max_col=4)
cats2 = Reference(ws, min_col=1, min_row=32, max_row=31+len(criteria_stats))
chart2.add_data(data2, titles_from_data=False)
chart2.set_categories(cats2)
chart2.height = 10
chart2.width = 20
ws.add_chart(chart2, "F30")
# Информация о дате создания
ws['A50'] = f"📅 Отчет создан: {datetime.now().strftime('%d.%m.%Y %H:%M')}"
ws['A50'].font = Font(name='Arial', size=10, italic=True, color='666666')
# Настройка ширины колонок
column_widths = [30, 25, 10, 12, 20, 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['website'] or hotel['website_address'])
ws.cell(row=i, column=3, value='Да' if hotel['has_website'] else 'Нет')
ws.cell(row=i, column=4, value=f"{hotel['total_score']}/{hotel['max_score']}")
ws.cell(row=i, column=5, value=f"{hotel['score_percentage']:.1f}%")
ws.cell(row=i, column=6, value=hotel['owner_inn'])
ws.cell(row=i, column=7, value=hotel['owner_ogrn'])
ws.cell(row=i, column=8, value=str(hotel['addresses']) if hotel['addresses'] else '')
ws.cell(row=i, column=9, value=hotel['phone'])
ws.cell(row=i, column=10, value=hotel['email'])
ws.cell(row=i, column=11, value=hotel['website_status'])
ws.cell(row=i, column=12, value=hotel['rkn_registry_status'])
ws.cell(row=i, column=13, value=hotel['audit_date'].strftime('%d.%m.%Y %H:%M') if hotel['audit_date'] else '')
# Цветовое кодирование процента
percentage = hotel['score_percentage'] or 0
if percentage >= 50:
fill_color = 'C6EFCE' # Зеленый
elif percentage >= 30:
fill_color = 'FFEB9C' # Желтый
else:
fill_color = 'FFC7CE' # Красный
ws.cell(row=i, column=5).fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')
# Форматирование
for col in range(1, 14):
ws.cell(row=i, column=col).font = normal_font
ws.cell(row=i, column=col).alignment = Alignment(horizontal='center')
# Настройка ширины колонок
column_widths = [30, 25, 10, 10, 10, 15, 15, 30, 15, 20, 15, 15, 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 create_criteria_sheet(workbook, audit_data):
"""Создать лист с детальными результатами по критериям"""
ws = workbook.create_sheet("🎯 Критерии")
# Стили
header_font = Font(name='Arial', size=10, bold=True, color='FFFFFF')
header_fill = PatternFill(start_color='366092', end_color='366092', fill_type='solid')
normal_font = Font(name='Arial', size=8)
# Заголовки
headers = ['Отель', 'Критерий', 'Найдено', 'Балл', 'Уверенность', 'URL', 'Цитата']
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')
row = 2
for hotel in audit_data:
if hotel['criteria_results']:
criteria = hotel['criteria_results']
for criterion in criteria:
ws.cell(row=row, column=1, value=hotel['hotel_name'])
ws.cell(row=row, column=2, value=criterion.get('criterion_name', ''))
ws.cell(row=row, column=3, value='Да' if criterion.get('found', False) else 'Нет')
ws.cell(row=row, column=4, value=criterion.get('score', 0))
ws.cell(row=row, column=5, value=criterion.get('final_confidence', ''))
ws.cell(row=row, column=6, value=criterion.get('ai_agent', {}).get('url', ''))
ws.cell(row=row, column=7, value=criterion.get('ai_agent', {}).get('quote', '')[:100] + '...' if criterion.get('ai_agent', {}).get('quote', '') else '')
# Цветовое кодирование
if criterion.get('found', False):
fill_color = 'C6EFCE' # Зеленый
else:
fill_color = 'FFC7CE' # Красный
ws.cell(row=row, column=3).fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type='solid')
# Форматирование
for col in range(1, 8):
ws.cell(row=row, column=col).font = normal_font
ws.cell(row=row, column=col).alignment = Alignment(horizontal='center')
row += 1
# Настройка ширины колонок
column_widths = [25, 30, 8, 8, 12, 25, 50]
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))}{row-1}"
# Заморозка заголовков
ws.freeze_panes = "A2"
def main():
"""Основная функция"""
print("🏔️ СОЗДАНИЕ ОТЧЕТА ПО ЧУКОТКЕ")
print("=" * 50)
# Получаем данные
print("📊 Загружаем данные из БД...")
audit_data, criteria_stats = get_chukotka_data()
print(f"✅ Загружено:")
print(f" 🏨 Отелей: {len(audit_data)}")
print(f" 🎯 Критериев: {len(criteria_stats)}")
# Создаем Excel файл
print("\n📝 Создаем Excel файл...")
workbook = Workbook()
# Лист дашборда
print("📊 Создаем дашборд...")
create_dashboard_sheet(workbook, audit_data, criteria_stats)
# Лист аудита
print("🏨 Создаем таблицу аудита...")
create_audit_sheet(workbook, audit_data)
# Лист критериев
print("🎯 Создаем детальные критерии...")
create_criteria_sheet(workbook, audit_data)
# Сохраняем файл
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"chukotka_audit_report_{timestamp}.xlsx"
workbook.save(filename)
print(f"\n✅ Отчет сохранен: {filename}")
print(f"📊 Листы:")
print(f" 📈 Дашборд Чукотка - графики и статистика")
print(f" 🏨 Детальный аудит - таблица отелей")
print(f" 🎯 Критерии - детальные результаты по критериям")
if __name__ == "__main__":
main()