📊 Improve dashboard with detailed registry statistics
- Added detailed statistics from hotel registry - Shows total hotels, active hotels, hotels with websites, accessible sites - Dynamic section positioning (no more hardcoded row numbers) - Fixed region name usage throughout (no more 'СПб' in Orel reports) - Shows different stats for ONLY_ACTIVE mode Stats now include: - Registry data: Total/Active hotels, Hotels with websites, Accessible sites - Audit data: Audits conducted, With/Without sites, RKN registry, Avg score, Compliant hotels (≥50%) Tested on Orel region: - 64 total active hotels in registry - 35 with websites - 29 audited - Avg score: 44.1%
This commit is contained in:
@@ -144,7 +144,9 @@ def get_region_data():
|
|||||||
def create_dashboard_sheet(workbook, audit_data, criteria_stats):
|
def create_dashboard_sheet(workbook, audit_data, criteria_stats):
|
||||||
"""Создать лист дашборда"""
|
"""Создать лист дашборда"""
|
||||||
ws = workbook.active
|
ws = workbook.active
|
||||||
ws.title = "📊 Дашборд СПб"
|
# Название листа по региону
|
||||||
|
region_short = REGION.replace('г. ', '').replace('область', 'обл.')[:15]
|
||||||
|
ws.title = f"📊 {region_short}"
|
||||||
|
|
||||||
# Стили
|
# Стили
|
||||||
header_font = Font(name='Arial', size=14, bold=True, color='FFFFFF')
|
header_font = Font(name='Arial', size=14, bold=True, color='FFFFFF')
|
||||||
@@ -163,7 +165,28 @@ def create_dashboard_sheet(workbook, audit_data, criteria_stats):
|
|||||||
ws['A3'].font = subheader_font
|
ws['A3'].font = subheader_font
|
||||||
ws['A3'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
|
ws['A3'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
|
||||||
|
|
||||||
# Подсчитываем статистику
|
# Получаем статистику из реестра (БД)
|
||||||
|
conn = psycopg2.connect(**DB_CONFIG, cursor_factory=RealDictCursor)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# Статистика по региону из реестра
|
||||||
|
status_filter_sql = "AND status_name = 'Действует'" if ONLY_ACTIVE else ""
|
||||||
|
cur.execute(f"""
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as total_in_registry,
|
||||||
|
COUNT(CASE WHEN status_name = 'Действует' THEN 1 END) as active_hotels,
|
||||||
|
COUNT(CASE WHEN website_address IS NOT NULL AND website_address != '' THEN 1 END) as with_websites,
|
||||||
|
COUNT(CASE WHEN website_status = 'accessible' THEN 1 END) as accessible_websites
|
||||||
|
FROM hotel_main
|
||||||
|
WHERE region_name = %s
|
||||||
|
{status_filter_sql}
|
||||||
|
""", (REGION,))
|
||||||
|
|
||||||
|
registry_stats = cur.fetchone()
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Подсчитываем статистику из аудита
|
||||||
total_hotels = len(audit_data)
|
total_hotels = len(audit_data)
|
||||||
total_with_website = sum(1 for h in audit_data if h['has_website'])
|
total_with_website = sum(1 for h in audit_data if h['has_website'])
|
||||||
total_without_website = total_hotels - total_with_website
|
total_without_website = total_hotels - total_with_website
|
||||||
@@ -171,70 +194,83 @@ def create_dashboard_sheet(workbook, audit_data, criteria_stats):
|
|||||||
total_compliant = sum(1 for h in audit_data if h['score_percentage'] >= 50)
|
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
|
avg_score = sum(h['score_percentage'] for h in audit_data) / total_hotels if total_hotels > 0 else 0
|
||||||
|
|
||||||
ws['A4'] = f"Всего отелей в {REGION}: {total_hotels}"
|
# Выводим детальную статистику
|
||||||
ws['A5'] = f"С сайтами: {total_with_website}"
|
row = 4
|
||||||
ws['A6'] = f"Без сайтов: {total_without_website}"
|
ws[f'A{row}'] = f"📋 По данным реестра в {REGION}:"
|
||||||
ws['A7'] = f"Сайты доступны для анализа: {total_with_website}"
|
ws[f'A{row}'].font = Font(name='Arial', size=10, bold=True)
|
||||||
ws['A8'] = f"Сайты недоступны: 0"
|
row += 1
|
||||||
ws['A9'] = f"В реестре РКН: {total_in_rkn}"
|
|
||||||
ws['A10'] = f"Проведено аудитов: {total_hotels}"
|
|
||||||
ws['A11'] = f"Средний балл (аудит): {avg_score:.1f}%"
|
|
||||||
|
|
||||||
for cell in ['A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11']:
|
if ONLY_ACTIVE:
|
||||||
ws[cell].font = normal_font
|
ws[f'A{row}'] = f" • Всего действующих отелей: {registry_stats['total_in_registry']}"
|
||||||
|
else:
|
||||||
|
ws[f'A{row}'] = f" • Всего отелей в реестре: {registry_stats['total_in_registry']}"
|
||||||
|
row += 1
|
||||||
|
ws[f'A{row}'] = f" • Из них действующих: {registry_stats['active_hotels']}"
|
||||||
|
|
||||||
# Категория
|
row += 1
|
||||||
ws['A13'] = "Категория"
|
ws[f'A{row}'] = f" • Отелей с указанными сайтами: {registry_stats['with_websites']}"
|
||||||
ws['A13'].font = subheader_font
|
row += 1
|
||||||
ws['A13'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
|
ws[f'A{row}'] = f" • Доступных сайтов (на момент проверки): {registry_stats['accessible_websites']}"
|
||||||
|
|
||||||
ws['A14'] = f"Сайты доступны: {total_with_website}"
|
row += 2
|
||||||
ws['B14'] = total_with_website
|
ws[f'A{row}'] = f"🔍 Проведено аудитов: {total_hotels}"
|
||||||
ws['A15'] = f"Сайты недоступны: 0"
|
ws[f'A{row}'].font = Font(name='Arial', size=10, bold=True)
|
||||||
ws['B15'] = 0
|
row += 1
|
||||||
ws['A16'] = f"Без сайтов: {total_without_website}"
|
ws[f'A{row}'] = f" • С сайтами: {total_with_website}"
|
||||||
ws['B16'] = total_without_website
|
row += 1
|
||||||
ws['A17'] = f"В реестре РКН: {total_in_rkn}"
|
ws[f'A{row}'] = f" • Без сайтов: {total_without_website}"
|
||||||
ws['B17'] = total_in_rkn
|
row += 1
|
||||||
|
ws[f'A{row}'] = f" • В реестре РКН: {total_in_rkn}"
|
||||||
|
row += 1
|
||||||
|
ws[f'A{row}'] = f" • Средний балл соответствия: {avg_score:.1f}%"
|
||||||
|
row += 1
|
||||||
|
ws[f'A{row}'] = f" • Отелей с баллом ≥50%: {total_compliant}"
|
||||||
|
|
||||||
for cell in ['A14', 'A15', 'A16', 'A17']:
|
# Форматирование
|
||||||
ws[cell].font = normal_font
|
for r in range(4, row + 1):
|
||||||
|
ws[f'A{r}'].font = normal_font
|
||||||
|
|
||||||
# Статистика по критериям
|
# Статистика по критериям (сдвигаем вниз)
|
||||||
ws['A19'] = "🎯 СТАТИСТИКА ПО КРИТЕРИЯМ"
|
criteria_start_row = row + 3
|
||||||
ws['A19'].font = subheader_font
|
ws[f'A{criteria_start_row}'] = "🎯 СТАТИСТИКА ПО КРИТЕРИЯМ"
|
||||||
ws['A19'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
|
ws[f'A{criteria_start_row}'].font = subheader_font
|
||||||
|
ws[f'A{criteria_start_row}'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
|
||||||
|
|
||||||
# Заголовки таблицы критериев
|
# Заголовки таблицы критериев
|
||||||
criteria_headers = ['Критерий', 'Найдено', 'Не найдено']
|
criteria_headers = ['Критерий', 'Найдено', 'Не найдено']
|
||||||
|
header_row = criteria_start_row + 1
|
||||||
for i, header in enumerate(criteria_headers, 1):
|
for i, header in enumerate(criteria_headers, 1):
|
||||||
cell = ws.cell(row=20, column=i, value=header)
|
cell = ws.cell(row=header_row, column=i, value=header)
|
||||||
cell.font = header_font
|
cell.font = header_font
|
||||||
cell.fill = header_fill
|
cell.fill = header_fill
|
||||||
cell.alignment = Alignment(horizontal='center')
|
cell.alignment = Alignment(horizontal='center')
|
||||||
|
|
||||||
# Данные по критериям
|
# Данные по критериям
|
||||||
for i, criterion in enumerate(criteria_stats, 21):
|
data_start_row = header_row + 1
|
||||||
|
for idx, criterion in enumerate(criteria_stats):
|
||||||
|
current_row = data_start_row + idx
|
||||||
not_found = criterion['total_checks'] - criterion['found_count']
|
not_found = criterion['total_checks'] - criterion['found_count']
|
||||||
|
|
||||||
ws.cell(row=i, column=1, value=criterion['criterion_name'])
|
ws.cell(row=current_row, column=1, value=criterion['criterion_name'])
|
||||||
ws.cell(row=i, column=2, value=criterion['found_count'])
|
ws.cell(row=current_row, column=2, value=criterion['found_count'])
|
||||||
ws.cell(row=i, column=3, value=not_found)
|
ws.cell(row=current_row, column=3, value=not_found)
|
||||||
|
|
||||||
# Форматирование
|
# Форматирование
|
||||||
for col in range(1, 4):
|
for col in range(1, 4):
|
||||||
ws.cell(row=i, column=col).font = normal_font
|
ws.cell(row=current_row, column=col).font = normal_font
|
||||||
ws.cell(row=i, column=col).alignment = Alignment(horizontal='center')
|
ws.cell(row=current_row, column=col).alignment = Alignment(horizontal='center')
|
||||||
|
|
||||||
# Распределение по баллам
|
# Распределение по баллам
|
||||||
ws['A40'] = "📊 РАСПРЕДЕЛЕНИЕ ПО БАЛЛАМ"
|
scores_start_row = data_start_row + len(criteria_stats) + 2
|
||||||
ws['A40'].font = subheader_font
|
ws[f'A{scores_start_row}'] = "📊 РАСПРЕДЕЛЕНИЕ ПО БАЛЛАМ"
|
||||||
ws['A40'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
|
ws[f'A{scores_start_row}'].font = subheader_font
|
||||||
|
ws[f'A{scores_start_row}'].fill = PatternFill(start_color='E7E6E6', end_color='E7E6E6', fill_type='solid')
|
||||||
|
|
||||||
# Заголовки
|
# Заголовки
|
||||||
score_headers = ['Диапазон', 'Количество']
|
score_headers = ['Диапазон', 'Количество']
|
||||||
|
score_header_row = scores_start_row + 1
|
||||||
for i, header in enumerate(score_headers, 1):
|
for i, header in enumerate(score_headers, 1):
|
||||||
cell = ws.cell(row=41, column=i, value=header)
|
cell = ws.cell(row=score_header_row, column=i, value=header)
|
||||||
cell.font = header_font
|
cell.font = header_font
|
||||||
cell.fill = header_fill
|
cell.fill = header_fill
|
||||||
cell.alignment = Alignment(horizontal='center')
|
cell.alignment = Alignment(horizontal='center')
|
||||||
@@ -247,14 +283,16 @@ def create_dashboard_sheet(workbook, audit_data, criteria_stats):
|
|||||||
('76-100%', sum(1 for h in audit_data if h['score_percentage'] >= 76))
|
('76-100%', sum(1 for h in audit_data if h['score_percentage'] >= 76))
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, (range_name, count) in enumerate(score_ranges, 42):
|
score_data_start = score_header_row + 1
|
||||||
ws.cell(row=i, column=1, value=range_name)
|
for idx, (range_name, count) in enumerate(score_ranges):
|
||||||
ws.cell(row=i, column=2, value=count)
|
current_row = score_data_start + idx
|
||||||
|
ws.cell(row=current_row, column=1, value=range_name)
|
||||||
|
ws.cell(row=current_row, column=2, value=count)
|
||||||
|
|
||||||
# Форматирование
|
# Форматирование
|
||||||
for col in range(1, 3):
|
for col in range(1, 3):
|
||||||
ws.cell(row=i, column=col).font = normal_font
|
ws.cell(row=current_row, column=col).font = normal_font
|
||||||
ws.cell(row=i, column=col).alignment = Alignment(horizontal='center')
|
ws.cell(row=current_row, column=col).alignment = Alignment(horizontal='center')
|
||||||
|
|
||||||
# Графики
|
# Графики
|
||||||
# Круговой график статуса сайтов
|
# Круговой график статуса сайтов
|
||||||
|
|||||||
Reference in New Issue
Block a user