262 lines
10 KiB
Python
262 lines
10 KiB
Python
|
|
#!/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()
|