- Краулеры: 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
174 lines
7.1 KiB
Python
174 lines
7.1 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Упрощенный веб-интерфейс без сложных БД запросов
|
||
"""
|
||
|
||
from fastapi import FastAPI
|
||
from fastapi.responses import HTMLResponse
|
||
import uvicorn
|
||
|
||
app = FastAPI(title="Система аудита отелей")
|
||
|
||
@app.get("/")
|
||
async def root():
|
||
return HTMLResponse("""
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Аудит Отелей - Общественный Контроль</title>
|
||
<style>
|
||
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
|
||
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; }
|
||
h1 { color: #2c3e50; text-align: center; }
|
||
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }
|
||
.stat-card { background: #3498db; color: white; padding: 20px; border-radius: 8px; text-align: center; }
|
||
.stat-number { font-size: 2em; font-weight: bold; }
|
||
.stat-label { font-size: 0.9em; opacity: 0.9; }
|
||
.regions { margin-top: 30px; }
|
||
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
|
||
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
|
||
th { background: #34495e; color: white; }
|
||
.btn { background: #e74c3c; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; }
|
||
.btn:hover { background: #c0392b; }
|
||
.btn-small { background: #27ae60; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; font-size: 0.8em; }
|
||
.btn-small:hover { background: #229954; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1>🏨 Система аудита отелей</h1>
|
||
|
||
<div class="stats">
|
||
<div class="stat-card">
|
||
<div class="stat-number" id="total-hotels">33,773</div>
|
||
<div class="stat-label">Всего отелей</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-number" id="crawled-sites">115</div>
|
||
<div class="stat-label">Спарсено сайтов</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-number" id="audited-hotels">239</div>
|
||
<div class="stat-label">Проведено аудитов</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-number" id="avg-score">11.0</div>
|
||
<div class="stat-label">Средний балл</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="regions">
|
||
<h2>📊 Статус по регионам</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Регион</th>
|
||
<th>Отелей</th>
|
||
<th>Спарсено</th>
|
||
<th>Проверено</th>
|
||
<th>Средний балл</th>
|
||
<th>Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Камчатский край</td>
|
||
<td>159</td>
|
||
<td>82</td>
|
||
<td>159</td>
|
||
<td>10.8/20</td>
|
||
<td><button class="btn-small" onclick="downloadAudit('Камчатский край')">📥 Скачать</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Орловская область</td>
|
||
<td>68</td>
|
||
<td>29</td>
|
||
<td>68</td>
|
||
<td>10.6/20</td>
|
||
<td><button class="btn-small" onclick="downloadAudit('Орловская область')">📥 Скачать</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Чукотский автономный округ</td>
|
||
<td>12</td>
|
||
<td>4</td>
|
||
<td>12</td>
|
||
<td>11.7/20</td>
|
||
<td><span style="color: #999;">Старый аудит</span></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div style="margin-top: 30px; text-align: center;">
|
||
<button class="btn" onclick="location.reload()">🔄 Обновить</button>
|
||
<button class="btn" onclick="alert('Полная версия: http://localhost:8888')">🔗 Полная версия</button>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function downloadAudit(regionName) {
|
||
const url = `/api/audit/download/${encodeURIComponent(regionName)}`;
|
||
const link = document.createElement('a');
|
||
link.href = url;
|
||
link.download = `audit_${regionName.replace(/\\s+/g, '_')}.xlsx`;
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|
||
""")
|
||
|
||
@app.get("/api/stats")
|
||
async def get_stats():
|
||
"""Упрощенная статистика"""
|
||
return {
|
||
"total_hotels": 33773,
|
||
"crawled_sites": 115,
|
||
"audited_hotels": 239,
|
||
"avg_score": 11.0,
|
||
"regions": [
|
||
{"region_name": "Камчатский край", "total_hotels": 159, "crawled": 82, "audited": 159, "avg_score": 10.8},
|
||
{"region_name": "Орловская область", "total_hotels": 68, "crawled": 29, "audited": 68, "avg_score": 10.6},
|
||
{"region_name": "Чукотский автономный округ", "total_hotels": 12, "crawled": 4, "audited": 12, "avg_score": 11.7}
|
||
]
|
||
}
|
||
|
||
@app.get("/api/audit/download/{region}")
|
||
async def download_audit(region: str):
|
||
"""Скачать Excel отчет по аудиту"""
|
||
import os
|
||
from fastapi.responses import FileResponse
|
||
|
||
# Ищем последний файл аудита для региона
|
||
region_safe = region.replace(' ', '_')
|
||
audit_dir = '/root/engine/public_oversight/hotels'
|
||
|
||
try:
|
||
files = [f for f in os.listdir(audit_dir) if f.startswith(f'audit_{region_safe}') and f.endswith('.xlsx')]
|
||
|
||
if not files:
|
||
return {"error": f"Файл аудита для региона '{region}' не найден. Сначала запустите аудит."}
|
||
|
||
# Берем последний файл (по дате в имени)
|
||
files.sort(reverse=True)
|
||
latest_file = files[0]
|
||
file_path = os.path.join(audit_dir, latest_file)
|
||
|
||
return FileResponse(
|
||
path=file_path,
|
||
filename=latest_file,
|
||
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||
)
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
if __name__ == "__main__":
|
||
uvicorn.run(app, host="0.0.0.0", port=8889)
|
||
|
||
|
||
|